// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// f32test\server\t_rand.cpp
// 
//

#include <f32file.h>
#include <e32test.h>
#include <e32math.h>
#include <e32hal.h>
#include "t_server.h"

GLDEF_D RTest test(_L("T_RAND"));

LOCAL_D TBuf8<512> testBuf(512);
LOCAL_D TInt64 	TheSeed=917824;
LOCAL_D TInt KMaxIteration;
LOCAL_D const TInt KMaxFiles=4;
LOCAL_D const TInt KMaxLengthIncrement=7770;
LOCAL_D const TInt mult[] = { 1, 5, 13, 37};
LOCAL_D const TInt KReduceSizeFrequency=20; // 1 reduce in ?? iterations
LOCAL_D const TInt KCheckFileFrequency=20000; // 1 check in ?? iterations
LOCAL_D const TInt KMaxBufferLength=0x8000;

LOCAL_C void WriteCluster(RFile& aFile,TInt aCluster)
//
// Extend aFile by 1 cluster
//
	{

	TUint8* bufPtr=(TUint8*)testBuf.Ptr();
	testBuf.SetLength(testBuf.MaxSize());
	Mem::Fill(bufPtr,testBuf.MaxSize(),aCluster);
	TInt r=aFile.Write(testBuf);
	test(r==KErrNone);
	}

LOCAL_C void SeekToCluster(RFile& aFile,TInt aCluster)
//
// Seek to aCluster and check it is found correctly
//
	{
	TBuf8<508> seekBuf(508);
	TInt r=aFile.Read(aCluster*testBuf.MaxSize(),seekBuf);
	test(r==KErrNone);
	test(seekBuf[0]==(TUint8)aCluster && seekBuf[507]==(TUint8)aCluster);
	}

LOCAL_C void SeekToCluster(RFile& aFile,TInt aCluster1,TInt aCluster2)
//
// Seek to aCluster and check it is found correctly
//
	{
	TBuf8<508> seekBuf(508);
	TInt r=aFile.Read(aCluster1*testBuf.MaxSize(),seekBuf);
	test(r==KErrNone);
	test(seekBuf[0]==(TUint8)aCluster1 && seekBuf[507]==(TUint8)aCluster1);
	r=aFile.Read(aCluster2*testBuf.MaxSize(),seekBuf);
	test(r==KErrNone);
	test(seekBuf[0]==(TUint8)aCluster2 && seekBuf[507]==(TUint8)aCluster2);
	}

LOCAL_C void ExhaustiveTest(RFile& aFile,TInt aCount1)
//
// Test every possible seeking combination
//
	{

	TInt i=0,k=0;
	for(k=0;k<aCount1;k++)
		{
		for(i=aCount1-1;i>0;i--)
			{
			SeekToCluster(aFile,i);
			SeekToCluster(aFile,k);
			}
		test.Printf(_L("Seek from %d          \r"),k);
		}
		test.Printf(_L("\n"));
	}

LOCAL_C void Test1()
//
// Test openning a large file
//
	{

	test.Next(_L("Create interleaved files"));
	RFile f1,f2;
//
	TInt r=f1.Replace(TheFs,_L("BIGFILE1.TST"),EFileWrite);
	test(r==KErrNone);
	r=f2.Replace(TheFs,_L("BIGFILE2.TST"),EFileWrite);
	test(r==KErrNone);
//
	TInt maxListLength=4;
	TInt i=0,k=0;
	TInt countf1=0;
	TInt countf2=0;
	for (k=0;k<maxListLength;k++)
		{
		for (i=0;i<maxListLength;i++)
			{
			TInt j;
			for (j=0;j<=i;j++)
				WriteCluster(f1,countf1++);
			for (j=0;j<=k;j++)
				WriteCluster(f2,countf2++);
			test.Printf(_L("Written %d to file1 %d to file2\n"),i+1,k+1);
			}
		}

	ExhaustiveTest(f1,countf1);
	ExhaustiveTest(f2,countf2);

	SeekToCluster(f1,1,10);
	SeekToCluster(f1,6,3);
	SeekToCluster(f1,8,4);
	SeekToCluster(f1,12,3);
	SeekToCluster(f1,23,32);
	SeekToCluster(f1,5,8);
	SeekToCluster(f1,7,9);
	SeekToCluster(f1,12,1);
	SeekToCluster(f1,2,32);
	SeekToCluster(f1,16,8);
	SeekToCluster(f1,9,5);
	SeekToCluster(f1,33,6);
	SeekToCluster(f1,13,7);
	SeekToCluster(f1,9,17);
	SeekToCluster(f1,4,5);
	SeekToCluster(f1,5,31);
	SeekToCluster(f1,11,10);
	SeekToCluster(f1,1,2);
	SeekToCluster(f1,5,5);

	f1.Close();
	f2.Close();
	r=TheFs.Delete(_L("BIGFile1.tst"));
	test(r==KErrNone);
	r=TheFs.Delete(_L("BIGFile2.tst"));
	test(r==KErrNone);
	CheckDisk();
	}

LOCAL_C void Test2()
//
// Reproduce old bugs
//
	{

	test.Next(_L("Regression Protection"));
	RFile f1,f2;
//
	TInt r=f1.Replace(TheFs,_L("BIGFILE1.TST"),EFileWrite);
	test(r==KErrNone);
	r=f2.Replace(TheFs,_L("BIGFILE2.TST"),EFileWrite);
	test(r==KErrNone);
//
	WriteCluster(f1,0);
	WriteCluster(f1,1);
	WriteCluster(f1,2);
	WriteCluster(f1,3);
	WriteCluster(f1,4);
	WriteCluster(f1,5);
	WriteCluster(f2,0);
	WriteCluster(f1,6);
//
	SeekToCluster(f1,6);
	SeekToCluster(f1,4);
//
	f1.Close();
	f2.Close();
	r=TheFs.Delete(_L("BIGFile1.tst"));
	test(r==KErrNone);
	r=TheFs.Delete(_L("BIGFile2.tst"));
	test(r==KErrNone);
	CheckDisk();
	}

LOCAL_C void Test3()
//
// Change file size while seeking
//
	{

	test.Next(_L("Alter filesize"));
	RFile f1;
	TheSeed=917824;
	TInt i=0,j=0;
//
	TInt r=f1.Replace(TheFs,_L("BIGFILE1.TST"),EFileWrite);
	test(r==KErrNone);
	
	r=f1.SetSize(65534);
	test(r==KErrNone);

	for(i=0;i<=15;i++)
		WriteCluster(f1,i);

	for (j=0;j<100;j++)
		{
		TInt cluster1=Math::Rand(TheSeed)%15;
		TInt cluster2=Math::Rand(TheSeed)%15;
		SeekToCluster(f1,cluster2,cluster1);
		}

	test.Next(_L("Increase Size"));
	r=f1.SetSize(1048577);
	test(r==KErrNone || r==KErrDiskFull);
	if (r==KErrDiskFull)
		{
		test.Printf(_L("File too big\n"));
		f1.Close();
		return;
		}

	test.Next(_L("Test data still present"));
	for (j=0;j<200;j++)
		{
		TInt cluster1=Math::Rand(TheSeed)%15;
		TInt cluster2=Math::Rand(TheSeed)%15;
		SeekToCluster(f1,cluster2,cluster1);
		}

	TInt newPos=8192;
	r=f1.Seek(ESeekStart,newPos);
	test(r==KErrNone);

	test.Next(_L("Write more data"));
	for(i=16;i<83;i++)
		WriteCluster(f1,i);

	test.Next(_L("Seek to new data"));
	for (j=0;j<200;j++)
		{
		TInt cluster1=Math::Rand(TheSeed)%83;
		TInt cluster2=Math::Rand(TheSeed)%83;
		SeekToCluster(f1,cluster2,cluster1);
		}

	test.Next(_L("Reduce file size"));
	r=f1.SetSize(135000);
	test(r==KErrNone);

	test.Next(_L("Test data still present"));
	for (j=0;j<200;j++)
		{
		TInt cluster1=Math::Rand(TheSeed)%31;
		TInt cluster2=Math::Rand(TheSeed)%31;
		SeekToCluster(f1,cluster2,cluster1);
		}

	f1.Close();
	}

class TFileReader
	{
public:
	TFileReader(RFile* aFile);
	void Next(TUint8& aVal,TInt& aLength);
	TBool Compare(TUint8 aVal,TInt aLength);
private:
	RFile iFile;
	TBuf8<512> iData;
	TInt iPos;
	};

TFileReader::TFileReader(RFile* aFile)
//
// Constructor
//
	: iFile(*aFile), iPos(0)
	{

	TInt r=iFile.Read(0,iData);
	test(r==KErrNone);
	}

void TFileReader::Next(TUint8& aVal,TInt& aLength)
//
// Read aLength contiguous bytes with aVal
//
	{

	if (iPos==iData.Length())
		{
		TInt r=iFile.Read(iData);
		test(r==KErrNone);
		iPos=0;
		if (iData.Length()==0)
			{
			aLength=0;
			return;
			}
		}

	aVal=iData[iPos];
	aLength=0;
	while(iPos<iData.Length())
		{
		if (iData[iPos]!=aVal)
			break;
		iPos++;
		aLength++;
		}
	}

TBool TFileReader::Compare(TUint8 aVal, TInt aLength)
//
// Compare file contents == aVal for aLength bytes
//
	{

	FOREVER
		{
		if(iPos==iData.Length())
			{
			TInt r=iFile.Read(iData);
			if (r!=KErrNone)
				{
				test.Printf(_L("READ error %d\n"),r);
				//test.Getch();
				RFs fs;
				r=fs.Connect();
				test.Printf(_L("connect returned %d\n"),r);
				//test.Getch();
				fs.Close();
				test(0);
				return(EFalse);
				}
			iPos=0;
			if (iData.Length()==0)
				{
				test.Printf(_L("\nFound Error\n"));
				test(0);
				//test.Getch();
				return(EFalse);
				}
			}
		while(iPos<iData.Length())
			{
			if (iData[iPos]!=aVal)
				{
				test.Printf(_L("\nFound Error\n"));
				test(0);
				//test.Getch();
				return(EFalse);
				}
			iPos++;
			aLength--;
			if (aLength==0)
				return(ETrue);
			}
		}
	}

LOCAL_C void CheckFileContents(RFile* aFile)
//
// Check all files have consistent contents
//
	{

	TFileReader f0(aFile);
	TFileReader f1(aFile+1);
	TFileReader f2(aFile+2);
	TFileReader f3(aFile+3);

	FOREVER
		{
		TUint8 val;
		TInt length;
		f0.Next(val,length);
		if (length==0)
			break;
		test(f1.Compare(val,length*mult[1]));
		test(f2.Compare(val,length*mult[2]));
		test(f3.Compare(val,length*mult[3]));
		}

	TUint8 val;
	TInt length;
	f1.Next(val,length);
	if (length!=0)
		{
		test.Printf(_L("\nFound Error\n"));
		test(0);
		//test.Getch();
		}
	test(length==0);
	f2.Next(val,length);
	if (length!=0)
		{
		test.Printf(_L("\nFound Error\n"));
		test(0);
		//test.Getch();
		}
	test(length==0);
	f3.Next(val,length);
	if (length!=0)
		{
		test.Printf(_L("\nFound Error\n"));
		test(0);
		//test.Getch();
		}
	test(length==0);
	}		
		
LOCAL_C void Test4()
//
// Read, write and resize 4 interleaved files
//
	{

	RFile f[KMaxFiles];
	HBufC8* dataBuf=HBufC8::NewL(KMaxBufferLength);

	TInt r=f[0].Replace(TheFs,_L("TEST1.DAT"),EFileWrite);
	test(r==KErrNone);
	r=f[1].Replace(TheFs,_L("TEST2.DAT"),EFileWrite);
	test(r==KErrNone);
	r=f[2].Replace(TheFs,_L("TEST3.DAT"),EFileWrite);
	test(r==KErrNone);
	r=f[3].Replace(TheFs,_L("TEST4.DAT"),EFileWrite);
	test(r==KErrNone);
	
	TInt size=0;
	TInt iteration=0;

	FOREVER
		{
		iteration++;
		TInt pos=(size) ? Math::Rand(TheSeed)%size : 0;
		TInt len=Math::Rand(TheSeed)%KMaxLengthIncrement;
		TInt order=Math::Rand(TheSeed)%KMaxFiles;
		TInt value=Math::Rand(TheSeed)%KMaxTUint8;
	
		TUint8* data=(TUint8*)dataBuf->Ptr();
		Mem::Fill(data,KMaxBufferLength,value);

		if (pos+len>size)
			size=pos+len;

		for (TInt i=0;i<KMaxFiles;i++)
			{
			TInt fileNum=(order+i)%KMaxFiles;
			TInt s=len*mult[fileNum];
			TInt filePos=pos*mult[fileNum];
			r=f[fileNum].Seek(ESeekStart,filePos);
			test(r==KErrNone);

			while(s>0)
				{
				TInt l=(s>KMaxBufferLength) ? KMaxBufferLength : s;
				dataBuf->Des().SetLength(l);
				r=f[fileNum].Write(*dataBuf);

				// Flush if write caching enabled to ensure we get disk space notifications
				if ((gDriveCacheFlags & EFileCacheWriteOn) && (r == KErrNone))
					r = f[fileNum].Flush();
			
				if (r==KErrDiskFull)
					goto End;
				test(r==KErrNone);
				s-=l;
				}
			
			}

		if ((iteration%KCheckFileFrequency)==0)
			CheckFileContents(&f[0]);

		test.Printf(_L("Iteration %d, size %d       \r"),iteration,size);
		if (iteration==KMaxIteration)
			break;
		
		if ((iteration%KReduceSizeFrequency)==0)
			{
			size=(size) ? Math::Rand(TheSeed)%size : 0;
			test.Printf(_L("\nReduceSize newsize=%d\n"),size);
			for (TInt i=0;i<KMaxFiles;i++)
				{
				TInt fileNum=(order+i)%KMaxFiles;
				r=f[fileNum].SetSize(size*mult[fileNum]);
				test(r==KErrNone);
				}
			CheckFileContents(&f[0]);
			}
		}
End:
	delete dataBuf;
	for (TInt i=0;i<KMaxFiles;i++)
		f[i].Close();
	test.Printf(_L("\n"));
	}

GLDEF_C void CallTestsL()
//
// Call all tests
//
	{

#if defined(__WINS__)
	if (gSessionPath[0]=='C')
		return;
#endif
	if (gSessionPath[0]=='C' || gSessionPath[0]=='Y')
		KMaxIteration=100;
	else
		KMaxIteration=100;
	CreateTestDirectory(_L("\\TRAND\\"));
	Test1();
	Test2();
	Test3();
	Test4();
	DeleteTestDirectory();
	}
