kerneltest/e32test/lffs/tf_read.cpp
author Slion
Tue, 08 Dec 2009 08:11:42 +0100
branchanywhere
changeset 19 f6d3d9676ee4
parent 0 a41df078684a
permissions -rw-r--r--
Trying to figure out how to implement my WINC like compatibility layer. Going the emulation way is probably not so smart. We should not use the kernel but rather hook native functions in the Exec calls.

// Copyright (c) 2000-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:
//

#include <e32std.h>
#include <e32std_private.h>
#include <e32svr.h>
#include <e32test.h>
#include "randgen.h"
#include "user_config.h"

RTest test( _L("TF_READ") );


const TInt KTestUserDataSize = 1024;
const TInt KBufferGuardSize = 16384;

const TInt KMaxWriteLength = 512;

const TInt64 KSampleDataRandomSeed = MAKE_TINT64(0x3e000111,0xAFCBDF0F);
const TInt64 KRandomTestSeed = MAKE_TINT64(0x90009901,0xABEF1011);

enum TPanicNo
	{
	EPanicGetDesOverflow,
	EPanicGetDesInitialOverflow,
	EPanicCheckOverflow
	};

LOCAL_D void Panic( TPanicNo aPanic )
	{
	_LIT( KPanicCat, "TF_READ" );
	User::Panic( KPanicCat, aPanic );
	}


class CCheckedBuffer : public CBase
	{
	public:
		CCheckedBuffer( TInt auserDataSize, TInt aGuardSize );
		~CCheckedBuffer();

		void CreateL();
		void InitialiseGuard();
		TBool CheckGuard( TInt aUserDataLength ) const;
		TBool CheckGuardAtStartOfUserData( TInt aGuardLength ) const;
		void GetDes( TPtrC8& aDes ) const;
		void GetDes( TPtr8& aDes, TInt aInitialLength, TInt aMaxLength ) const;
		

	private:
		CCheckedBuffer();

	private:
		TPtr8	iUserData;		// pointer to user data area
		const TInt	iUserDataSize;
		const TInt	iGuardSize;
		TUint8*	iAllocCell;
	};



CCheckedBuffer::CCheckedBuffer( TInt aUserDataSize, TInt aGuardSize )
	: iUserData(0,0), iUserDataSize( aUserDataSize ), iGuardSize( aGuardSize )
	{
	}

CCheckedBuffer::~CCheckedBuffer()
	{
	delete iAllocCell;
	}

void CCheckedBuffer::CreateL()
	{
	TInt totalCellSizeRequired = iUserDataSize + (2 * iGuardSize);

	iAllocCell = (TUint8*)User::AllocL( totalCellSizeRequired );

	test.Printf( _L("Allocated heap cell for checked buffer\n") );

	iUserData.Set( iAllocCell + iGuardSize, iUserDataSize, iUserDataSize );
	}

void CCheckedBuffer::GetDes( TPtrC8& aDes ) const
	//
	// Create descriptor to the whole user data area in aDes
	//
	{
	aDes.Set( iAllocCell + iGuardSize, iUserDataSize );
	}

void CCheckedBuffer::GetDes( TPtr8& aDes, TInt aInitialLength, TInt aMaxLength ) const
	//
	// Create modifiable descriptor to the user data area in aDes,
	// with a maximum length aMaxLength, and initial length aInitialLength
	//
	{
	__ASSERT_ALWAYS( aMaxLength <= iUserDataSize, Panic(EPanicGetDesOverflow) );
	__ASSERT_ALWAYS( aInitialLength <= iUserDataSize, Panic(EPanicGetDesInitialOverflow) );
	aDes.Set( iAllocCell + iGuardSize, aInitialLength, aMaxLength );
	}


void CCheckedBuffer::InitialiseGuard()
	//
	// Create the guard regions
	//
	{
	TInt totalCellSize = User::AllocLen( iAllocCell );
	Mem::Fill( iAllocCell, totalCellSize, 0x5A );
	}

TBool CCheckedBuffer::CheckGuard( TInt aUserDataLength ) const
	//
	// Checks that the guard value is still present before the user data
	// area, and after aUserDataLength bytes of user data
	//
	{
	const TUint8* p = iAllocCell;
	const TUint8* pUserDataStart = iUserData.Ptr();

	for( ; p < pUserDataStart; p++ )
		{
		if( 0x5a != *p )
			{
			return EFalse;
			}
		}

	p = pUserDataStart + aUserDataLength;
	const TUint8* pEnd = iAllocCell + User::AllocLen( iAllocCell );

	for( ; p < pEnd; p++ )
		{
		if( 0x5a != *p )
			{
			return EFalse;
			}
		}
	
	return ETrue;
	}


TBool CCheckedBuffer::CheckGuardAtStartOfUserData( TInt aGuardLength ) const
	//
	// Checks that the first aGuardLength bytes of the user data area
	// contain the guard value
	//
	{
	const TUint8* p = iUserData.Ptr();
	const TUint8* pEnd = p + aGuardLength;

	for( ; p < pEnd; p++ )
		{
		if( 0x5a != *p )
			{
			return EFalse;
			}
		}
	
	return ETrue;
	}



class CReadTest : public CBase
	{
	public:
		~CReadTest();

		void CreateL();

		void DoTest();

	private:
		static TInt DummyThread( TAny* aParam );

		void CreateSampleData();
		static TBool CheckZero( const TPtrC8& aDes );
		void CreateTestData( TInt aBlockNumber, TBool aEndOfBlock );
		TBool CompareAgainstFlash( TInt aFlashOffset, const TPtrC8& aDes, TInt aDescOffset );

		void TestSimpleReads();
		void TestSimpleThreadReads();
		void TestUnalignedReads();
		void TestUnalignedThreadReads();
		void TestOffsetBufferThreadReads();
		void TestOffsetBufferUnalignedThreadReads();
		void TestReadsFromAllBlocks();
		void TestSimpleScatterReads1();
		void TestSimpleScatterReads2();
		void TestScatterGather();
		void TestReadAcrossBlock();

		void PerformCheckedRead( TInt aReadPos, TInt aReadLen );
		void PerformCheckedThreadRead( TInt aReadPos, TInt aReadLen, TInt aDescOffset );

	private:
		TInt			iFlashSize;
		TInt			iBlockSize;
		TInt			iBlockCount;

		TBusLocalDrive	iDrive;
		TBool			iDriveOpened;
		TBuf8<512>		iReadBuffer;

		TRandomGenerator	iRandom;

		TBuf8<KTestUserDataSize> iSampleData;

		CCheckedBuffer*	iBuffer;

		RThread			iDummyThread;
	};

CReadTest::~CReadTest()
	{
	if( iDriveOpened )
		{
		iDrive.Disconnect();
		}
	}



void CReadTest::CreateL()
	{
	//
	// Load the device drivers
	//
	TInt r;

#ifndef SKIP_PDD_LOAD
	test.Printf( _L("Loading %S\n"), &KLfsDriverName );
	r = User::LoadPhysicalDevice( KLfsDriverName );
	test( KErrNone == r || KErrAlreadyExists == r );
#endif

#ifdef UNMOUNT_DRIVE
	RFs fs;
	test( KErrNone == fs.Connect() );
#if 0 // XXX - API violation on EKA2
	test( KErrNone == fs.SetDefaultPath( _L("Z:\\") ) );
#endif
	TFullName name;
	fs.FileSystemName( name, KLffsLogicalDriveNumber );
	if( name.Length() > 0 )
		{
		test.Printf( _L("Unmounting drive") );
		test( KErrNone == fs.DismountFileSystem( _L("Lffs"), KLffsLogicalDriveNumber) );
		User::After( 2000000 );
		test.Printf( _L("Drive unmounted") );
		}

	fs.Close();
#endif

	//
	// Open a TBusLogicalDevice to it
	//
	test.Printf( _L("Opening media channel\n") );
	TBool changedFlag = EFalse;
	r = iDrive.Connect( KDriveNumber, changedFlag );
	User::LeaveIfError( r );
	iDriveOpened = ETrue;

	//
	// Get size of Flash drive
	//
	TLocalDriveCapsV2Buf info;
    iDrive.Caps(info);
	iFlashSize = I64LOW(info().iSize);
	iBlockSize = info().iEraseBlockSize;
	iBlockCount = iFlashSize / iBlockSize;

	test.Printf( _L("Flash size is 0x%x bytes\n"), iFlashSize );

	//
	// Create a dummy thread that we can use to force
	// other-thread write operations
	//
#if 0
	test( KErrNone == iDummyThread.Create( _L("DUMMY"), DummyThread, 256, KMinHeapSize, KMinHeapSize, NULL ) );
#else
	// XXX TONYL
	test( KErrNone == iDummyThread.Create( _L("DUMMY"), DummyThread, KDefaultStackSize, KMinHeapSize, KMinHeapSize, NULL ) );

//	test.Printf( _L("== do it"));
//	TInt pas = iDummyThread.Create( _L("DUMMY"), DummyThread, KDefaultStackSize, KMinHeapSize, KMinHeapSize, NULL );
//	test.Printf( _L("CREATE = %d"), pas);
//	test (pas == KErrNone);
#endif
#if 1
	iDummyThread.Resume();
#endif

	//
	// Create a checked buffer
	//
	iBuffer = new(ELeave) CCheckedBuffer( KTestUserDataSize, KBufferGuardSize );
	iBuffer->CreateL();

	//
	// Seed the pseudo-random number generator
	//
	iRandom.SetSeed( KSampleDataRandomSeed );

	test.Printf( _L("CreateL complete\n") );
	}



TInt CReadTest::DummyThread( TAny* /* aParam */ )
	//
	// Thread does nothing at all
	//
	{
#if 1
	test.Printf( _L("== do it"));
#endif
	for(;;)
		{
		User::WaitForAnyRequest();	// just block
		}
	}


void CReadTest::TestSimpleReads()
	//
	// Makes reads of 1 byte to 512 bytes into the start of the
	// checked buffer and tests that only the expected bytes have changed
	// This uses the simple read function from TBusLocalDrive, and 
	// reads from an aligned Flash address
	//
	{
	test.Next( _L("Testing simple reads\n") );

	//
	// Descriptor to user data area, passed to media driver
	//
	TPtr8 des(0,0);

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		test.Printf( _L("Reading %d bytes\n"), readLen );
		
		//
		// Prepare the guard data
		//
		iBuffer->InitialiseGuard();
	
		//
		// Set up the descriptor, length=0, maxlen=readLen
		//
		iBuffer->GetDes( des, 0, readLen );

		//
		// Now read some data into it
		//
		test( KErrNone == iDrive.Read( 0, readLen, des ) );

		//
		// Check what we got
		//
		test( des.Length() == readLen );
		
		TPtrC8 newDes;

	iBuffer->GetDes( newDes );

		test( newDes.Ptr() == des.Ptr() );

		test( iBuffer->CheckGuard( readLen ) );

		test( CompareAgainstFlash( 0, des, 0 ) );

		}
	}

void CReadTest::TestSimpleThreadReads()
	//
	// Makes reads of 1 byte to 512 bytes into the start of the
	// checked buffer and tests that only the expected bytes have changed
	// This uses the more complex read function from TBusLocalDrive, and 
	// reads from an aligned Flash address
	//
	{
	test.Next( _L("Testing simple reads using other-thread read function\n") );

	//
	// Descriptor to user data area, passed to media driver
	//
	TPtr8 des(0,0);

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		test.Printf( _L("Reading %d bytes\n"), readLen );
		
		//
		// Prepare the guard data
		//
		iBuffer->InitialiseGuard();
		test.Printf( _L("AA\n"));
		
		//
		// Set up the descriptor, length=0, maxlen=readLen
		//
		iBuffer->GetDes( des, 0, readLen );
		test.Printf( _L("BB\n"));

		//
		// Now read some data into it
		//
		test( KErrNone == iDrive.Read( 0, readLen, &des, KLocalMessageHandle, 0 ) );
		test.Printf( _L("CC\n"));
#if 0
		test( KErrNone == iDrive.Read( 0, readLen, &des, iDummyThread.Handle(), 0 ) );
#else
		// XXX - this works
		test( KErrNone == iDrive.Read( 0, readLen, &des, KLocalMessageHandle, 0 ) );
#endif

		//
		// Check what we got
		//
		test.Printf( _L("DD\n"));
		test.Printf( _L("DD\n"));
		test.Printf( _L("DD\n"));
		test.Printf( _L("DD\n"));
		test( des.Length() == readLen );
		
		TPtrC8 newDes;
		test.Printf( _L("EE\n"));
		iBuffer->GetDes( newDes );
		test.Printf( _L("FF\n"));
		test( newDes.Ptr() == des.Ptr() );

		test( iBuffer->CheckGuard( readLen ) );

		test.Printf( _L("GG\n"));
		test( CompareAgainstFlash( 0, des, 0 ) );
		test.Printf( _L("HH\n"));

		}
	}


void CReadTest::TestUnalignedReads()
	//
	// Makes reads of 1 byte to 512 bytes into the start of the
	// checked buffer and tests that only the expected bytes have changed
	// This uses the simple read function from TBusLocalDrive.
	// The data is read from an unaligned address (0ffset 1, 2, 3)
	//
	{
	test.Next( _L("Testing unaligned reads\n") );

	//
	// Descriptor to user data area, passed to media driver
	//
	TPtr8 des(0,0);

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		//
		// Set up the descriptor, length=0, maxlen=readLen
		//
		iBuffer->GetDes( des, 0, readLen );

		//
		// Repeat for each offset
		//
		for( TInt offs = 1; offs < 4; offs++ )
			{
			test.Printf( _L("Reading %d unaligned bytes from offset %d\n"), readLen, offs );

			iBuffer->InitialiseGuard();
			test( KErrNone == iDrive.Read( offs, readLen, des ) );

			test( des.Length() == readLen );
			
			TPtrC8 newDes;
			iBuffer->GetDes( newDes );
			test( newDes.Ptr() == des.Ptr() );

			test( iBuffer->CheckGuard( readLen ) );

			test( CompareAgainstFlash( offs, des, 0 ) );
			}

		}
	}


void CReadTest::TestUnalignedThreadReads()
	//
	// Makes reads of 1 byte to 512 bytes into the start of the
	// checked buffer and tests that only the expected bytes have changed
	// This uses the thread read function from TBusLocalDrive.
	// The data is read from an unaligned address (0ffset 1, 2, 3)
	//
	{
	test.Next( _L("Testing unaligned other-thread reads\n") );

	//
	// Descriptor to user data area, passed to media driver
	//
	TPtr8 des(0,0);

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		//
		// Set up the descriptor, length=0, maxlen=readLen
		//
		iBuffer->GetDes( des, 0, readLen );

		//
		// Repeat for each offset
		//
		for( TInt offs = 1; offs < 4; offs++ )
			{
			test.Printf( _L("Reading %d unaligned bytes from offset %d\n"), readLen, offs );

			iBuffer->InitialiseGuard();
#if 0
			test( KErrNone == iDrive.Read( offs, readLen, &des, iDummyThread.Handle(), 0 ) );
#else
			test( KErrNone == iDrive.Read( offs, readLen, &des, KLocalMessageHandle, 0 ) );
#endif

			test( des.Length() == readLen );
			
			TPtrC8 newDes;
			iBuffer->GetDes( newDes );
			test( newDes.Ptr() == des.Ptr() );

			test( iBuffer->CheckGuard( readLen ) );

			test( CompareAgainstFlash( offs, des, 0 ) );
			}

		}
	}


void CReadTest::TestOffsetBufferThreadReads()
	//
	// Makes reads of 1 byte to 512 bytes to an offset position in the
	// checked buffer and tests that only the expected bytes have changed
	// This uses the more complex read function from TBusLocalDrive, and 
	// reads from an aligned Flash address
	//
	{
	test.Next( _L("Testing other-thread reads into offset position in descriptor\n") );

	//
	// Descriptor to user data area, passed to media driver
	//
	TPtr8 des(0,0);

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		test.Printf( _L("Reading %d bytes\n"), readLen );
		

		//
		// Repeat test for offsets 0..64 in buffer
		//
		for( TInt destOffset = 1; destOffset < 64; destOffset++ )
			{
//			test.Printf( _L("... dest offset = %d"), destOffset );

			//
			// Prepare the guard data
			//
			iBuffer->InitialiseGuard();
			
			//
			// Set up the descriptor, length=0, maxlen=readLen+destOffset
			//
			iBuffer->GetDes( des, 0, readLen + destOffset );

#if 0
			test( KErrNone == iDrive.Read( 0, readLen, &des, iDummyThread.Handle(), destOffset ) );
#else
			test( KErrNone == iDrive.Read( 0, readLen, &des, KLocalMessageHandle, destOffset ) );
#endif

			//
			// Check what we got
			//
			test( des.Length() == readLen + destOffset );
			
			TPtrC8 newDes;
			iBuffer->GetDes( newDes );
			test( newDes.Ptr() == des.Ptr() );

			//
			// end of written data is at readLen + destOffset
			//
			test( iBuffer->CheckGuard( readLen+destOffset ) );
			//
			// check the section between that start of the user data and
			// the offset position still contains guard data
			//
			test( iBuffer->CheckGuardAtStartOfUserData( destOffset ) );

			test( CompareAgainstFlash( 0, des, destOffset ) );
			}

		}
	}


void CReadTest::TestOffsetBufferUnalignedThreadReads()
	//
	// Makes reads of 1 byte to 512 bytes to an offset position in the
	// checked buffer and tests that only the expected bytes have changed
	// This uses the more complex read function from TBusLocalDrive, and 
	// reads from an aligned Flash address
	//
	{
	test.Next( _L("Testing other-thread unaligned reads into offset position in descriptor\n") );

	//
	// Descriptor to user data area, passed to media driver
	//
	TPtr8 des(0,0);

	for( TInt readLen = 1; readLen <= 500; readLen++ )
		{
		test.Printf( _L("Reading %d bytes\n"), readLen );
		

		//
		// Repeat test for offsets 0..64 in buffer
		//
		for( TInt destOffset = 1; destOffset < 64; destOffset++ )
			{
//			test.Printf( _L("... dest offset = %d"), destOffset );

			//
			// repeat for each source offset
			//
			for( TInt offs = 1; offs < 4; offs++ )
				{
				//
				// Prepare the guard data
				//
				iBuffer->InitialiseGuard();
				
				//
				// Set up the descriptor, length=0, maxlen=readLen+destOffset
				//
				iBuffer->GetDes( des, 0, readLen + destOffset );

#if 0
				test( KErrNone == iDrive.Read( offs, readLen, &des, iDummyThread.Handle(), destOffset ) );
#else
				test( KErrNone == iDrive.Read( offs, readLen, &des, KLocalMessageHandle, destOffset ) );
#endif


				//
				// Check what we got
				//
				test( des.Length() == readLen + destOffset );
				
				TPtrC8 newDes;
				iBuffer->GetDes( newDes );
				test( newDes.Ptr() == des.Ptr() );

				//
				// end of written data is at readLen + destOffset
				//
				test( iBuffer->CheckGuard( readLen+destOffset ) );
				//
				// check the section between that start of the user data and
				// the offset position still contains guard data
				//
				test( iBuffer->CheckGuardAtStartOfUserData( destOffset ) );

				test( CompareAgainstFlash( offs, des, destOffset ) );
				} // end for
			}
		}
	}


void CReadTest::PerformCheckedRead( TInt aReadPos, TInt aReadLen )
	{
	TPtr8 des(0,0);
	iBuffer->InitialiseGuard();
	iBuffer->GetDes( des, 0, aReadLen );

	test.Printf( _L("Reading %d byte(s) from offset 0x%x\n"), aReadLen, aReadPos );
	test( KErrNone == iDrive.Read( aReadPos, aReadLen, des ) );
	test( des.Length() == aReadLen );
	test( iBuffer->CheckGuard( aReadLen ) );
	test( CompareAgainstFlash( aReadPos, des, 0 ) );
	}

void CReadTest::PerformCheckedThreadRead( TInt aReadPos, TInt aReadLen, TInt aDescOffset )
	{
	TPtr8 des(0,0);
	iBuffer->InitialiseGuard();
	iBuffer->GetDes( des, 0, aReadLen + aDescOffset );

	test.Printf( _L("Reading %d byte(s) from offset 0x%x to thread descriptor offset %d\n"), aReadLen, aReadPos, aDescOffset );
#if 0
	test( KErrNone == iDrive.Read( aReadPos, aReadLen, &des, iDummyThread.Handle(), aDescOffset ) );
#else
	test( KErrNone == iDrive.Read( aReadPos, aReadLen, &des, KLocalMessageHandle, aDescOffset ) );
#endif

//	test.Printf( _L("Check descriptor length") );
	test( des.Length() == aReadLen + aDescOffset );
//	test.Printf( _L("Check guard") );
	test( iBuffer->CheckGuard( aReadLen + aDescOffset ) );
//	test.Printf( _L("Check guard at start of descriptor") );
	test( iBuffer->CheckGuardAtStartOfUserData( aDescOffset ) );
	test( CompareAgainstFlash( aReadPos, des, aDescOffset ) );
	}


void CReadTest::TestReadsFromAllBlocks()
	//
	// Does some spot-test reads from all blocks to make sure
	// that reading across the whole Flash works
	//
	{
	test.Next( _L("Testing reads from all blocks\n") );

	for( TInt block = 0; block < iBlockCount; block++ )
		{
		test.Printf( _L("Reading from block %d"), block );
		TInt readBase = (block * iBlockSize);
		
		PerformCheckedRead( readBase, 1 );
		PerformCheckedRead( readBase, 24 );
		PerformCheckedRead( readBase, 99 );
		PerformCheckedRead( readBase, 511 );
		PerformCheckedRead( readBase+1, 1 );
		PerformCheckedRead( readBase+1, 24 );
		PerformCheckedRead( readBase+1, 99 );
		PerformCheckedRead( readBase+1, 511 );
		PerformCheckedRead( readBase+3, 1 );
		PerformCheckedRead( readBase+3, 24 );
		PerformCheckedRead( readBase+3, 99 );
		PerformCheckedRead( readBase+3, 511 );

		PerformCheckedThreadRead( readBase, 1, 0 );
		PerformCheckedThreadRead( readBase, 24, 0 );
		PerformCheckedThreadRead( readBase, 99, 2 );
		PerformCheckedThreadRead( readBase, 511, 0 );
		PerformCheckedThreadRead( readBase+1, 1, 11 );
		PerformCheckedThreadRead( readBase+1, 24, 4 );
		PerformCheckedThreadRead( readBase+1, 99, 24 );
		PerformCheckedThreadRead( readBase+1, 511, 0 );
		PerformCheckedThreadRead( readBase+3, 1, 32 );
		PerformCheckedThreadRead( readBase+3, 24, 333 );
		PerformCheckedThreadRead( readBase+3, 99, 0 );
		PerformCheckedThreadRead( readBase+3, 511, 1 );
		}
	}

void CReadTest::TestSimpleScatterReads1()
	//
	// Does some simple reads of varying length from the
	// blocks in pseudo-random order.
	//
	{
	test.Next( _L("Testing simple scatter reads\n") );

	TRandomGenerator random;
	random.SetSeed( KRandomTestSeed );

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		TInt block = random.Next() % iBlockCount;
		test.Printf( _L("Reading block %d"), block );
		TInt readBase = (block * iBlockSize);
		PerformCheckedRead( readBase, readLen );
		}
	}

void CReadTest::TestSimpleScatterReads2()
	//
	// Does some simple reads of varying length from the
	// blocks in pseudo-random order.
	//
	// This is similar to TestSimpleScatterReads1 except that
	// as the length reduces the read position is moved along
	// and the test uses the thread-read variant
	//
	{
	test.Next( _L("Testing simple scatter reads\n") );

	TRandomGenerator random;
	random.SetSeed( KRandomTestSeed );

	for( TInt readLen = 1; readLen <= 512; readLen++ )
		{
		TInt block = random.Next() % iBlockCount;
		test.Printf( _L("Reading block %d"), block );
		TInt readBase = (block * iBlockSize) + (512 - readLen);
		PerformCheckedRead( readBase, readLen );
		}
	}

void CReadTest::TestScatterGather()
	//
	// This reads bytes from all over the Flash and concatenates
	// them into a single descriptor. This isn't representative of
	// anything a real filesystem would do (at present!) but
	// is an interesting test of the media driver
	//
	{
	test.Next( _L("Testing scatter-gather reads\n") );

	TRandomGenerator random;
	random.SetSeed( KRandomTestSeed );

	const TInt KMaxReads = 500;
	struct SReadInfo
		{
		TInt	iOffset;
		TInt	iLength;
		};

	SReadInfo* readInfoArray = new SReadInfo[KMaxReads];
	test( NULL != readInfoArray );

	TPtr8 des(0,0);
	iBuffer->InitialiseGuard();
	iBuffer->GetDes( des, 0, KTestUserDataSize );
	TInt descOffset = 0;

	TInt readCount;
	for( readCount = 0; readCount < KMaxReads; readCount++ )
		{
		//
		// Create random read position and length
		//
		TInt block = random.Next() % iBlockCount;
		TInt blockOffset = random.Next() % 1000;
		if( blockOffset > 500 )
			{
			blockOffset = iBlockSize - 1 - blockOffset;
			}
		TInt readOffset = (block * iBlockSize) + blockOffset;
		TInt readLength = (random.Next() % 8) + 1;

		if( des.Length() + readLength > des.MaxLength() )
			{
			break;	// finished
			}
		
		//
		// Save the position & length
		//
		readInfoArray[readCount].iOffset = readOffset;
		readInfoArray[readCount].iLength = readLength;

		//
		// do the read
		//
		_LIT( KScatterReadMsg, "Reading Flash @%x %d bytes to desc offset %d" );
		test.Printf( KScatterReadMsg, readOffset, readLength, descOffset );
#if 0
		test( KErrNone == iDrive.Read( readOffset, readLength, &des, iDummyThread.Handle(), descOffset ) );
#else
		test( KErrNone == iDrive.Read( readOffset, readLength, &des, KLocalMessageHandle, descOffset ) );
#endif
		test( des.Length() == descOffset + readLength );

		descOffset += readLength;
		}

	//
	// Now check all the data against the Flash contents
	//
	descOffset = 0;
	for( TInt i = 0; i < readCount; i++ )
		{
		TInt readOffset = readInfoArray[i].iOffset ;
		TInt readLength = readInfoArray[i].iLength;

		TPtrC8 ptr( des.Ptr() + descOffset, readLength );
		test( CompareAgainstFlash( readOffset, ptr, 0 ) );
		descOffset += readLength;
		}

	delete[] readInfoArray;

	}



void CReadTest::TestReadAcrossBlock()
	//
	// Test reads that cross a block boundary
	//
	{
	test.Next( _L("Testing reads across block boundary\n") );

	for( TInt block = 1; block < iBlockCount - 1; block++ )
		{
		for( TInt readLen = 2; readLen <= 1024; readLen++ )
			{
			TInt blockBase = (block * iBlockSize);
			TInt readOffs = blockBase + (iBlockSize - (readLen/2));
			PerformCheckedRead( readOffs, readLen );
			}
		}
	}



void CReadTest::CreateSampleData()
	//
	// Fills iSampleData with pseudo-random test data
	//
	{
	TUint32* p = (TUint32*)iSampleData.Ptr();
	for( TInt j = 0; j < KTestUserDataSize/4; j++ )
		{
		*p++ = iRandom.Next();
		}

	iSampleData.SetLength( KTestUserDataSize );
	}


TBool CReadTest::CheckZero( const TPtrC8& aDes )
	//
	// Checks that all bytes in aDes are zero
	//
	{
	for( TInt i = aDes.Length(); i > 0; )
		{
		--i;
		if( 0 != aDes[i] )
			{
			return EFalse;
			}
		}
	return ETrue;
	}



void CReadTest::CreateTestData( TInt aBlockNumber, TBool aEndOfBlock )
	//
	// Writes some test data to the Flash. If aEndOfBlock is EFalse the
	// data is created at the start of the block. If it is ETrue then
	// the data is created right at the end of the block
	//
	{

	test.Printf( _L("Writing test data to Flash block %d\n"), aBlockNumber );
	
	//
	// Generate some test data
	//
	CreateSampleData();

	test.Printf( _L("Erasing block") );
	TInt writeBaseOffset = (aBlockNumber * iBlockSize);
	test( KErrNone == iDrive.Format( writeBaseOffset, iBlockSize ) );

	
	TInt writeCount = iSampleData.Length() / KMaxWriteLength;
	TInt r = KErrNone;
	if( aEndOfBlock )
		{
		writeBaseOffset += iBlockSize - iSampleData.Length();
		}

	TInt writeOffset = writeBaseOffset;

	const TUint8* src = iSampleData.Ptr();

	test.Printf( _L("Writing data") );
	for( ; (writeCount > 0) && (KErrNone == r); writeCount-- )
		{
		TPtrC8 buf( src, KMaxWriteLength );
		test( KErrNone == iDrive.Write( writeOffset, buf ) );
		writeOffset += KMaxWriteLength;
		src += KMaxWriteLength;
		}
	test( r == KErrNone );

	//
	// check that the data was written ok
	//
	test.Printf( _L("Verifying data") );
	test( CompareAgainstFlash( writeBaseOffset, iSampleData, 0 ) );

	test.Printf( _L("... test data written\n") );
	}

TBool CReadTest::CompareAgainstFlash( TInt aFlashOffset, const TPtrC8& aDes, TInt aDescOffset )
	//
	// Checks that the data in aDes matches that in the Flash at position
	// aFlashOffset.
	// The test starts at offset aDescOffset in aSampleData. The data length
	// tested is aDes->Length() - aDescOffset
	//
	{
	TInt dataLength = aDes.Length() - aDescOffset;
	const TUint8* srcPtr = aDes.Ptr() + aDescOffset;

	TUint offset = aFlashOffset;
	
	TBool failed = EFalse;
	const TInt readBufLen = iReadBuffer.MaxLength();

	while( (dataLength > 0) && !failed )
		{
		TInt len = Min( dataLength, readBufLen );
		TInt r = iDrive.Read( offset, len, iReadBuffer );
		if( r != KErrNone )
			{
			test.Printf( _L("... FAIL: read failed (%d) at offset 0x%x\n"), r, offset );
			test( KErrNone == r );
			}
		test( iReadBuffer.Length() == len );

		if( 0 != Mem::Compare( srcPtr, len, iReadBuffer.Ptr(), len ) )
			{
			test.Printf( _L("... FAIL: mismatch around offset 0x%x\n"), offset );
			failed = ETrue;
			}
		offset += len;
		dataLength -= len;
		srcPtr += len;
		}
	
	return !failed;
	}



void CReadTest::DoTest()
	//
	// Main test dispatcher
	//
	{
	//
	// Create some test data at start of block 0
	//
	CreateTestData( 0, EFalse );

	//
	// Now do the simple tests, all reads will return zeros
	//
#if 0
	TestSimpleReads();
#endif
	TestSimpleThreadReads();
	TestUnalignedReads();
	TestUnalignedThreadReads();
	TestOffsetBufferThreadReads();
	TestOffsetBufferUnalignedThreadReads();

	//
	// Create some more data at start of all other blocks
	//
	test.Next( _L("Creating more test data in other blocks") );
	for( TInt i = 1; i < iBlockCount; i++ )
		{
		CreateTestData( i, EFalse );
		}

	//
	// Make sure we can read valid data out of the other blocks
	//
	TestReadsFromAllBlocks();

	//
	// Now do some scatter-read tests
	//
	TestSimpleScatterReads1();
	TestSimpleScatterReads2();

	//
	// Create some more testdata at end of all blocks
	//
	test.Next( _L("Creating test data at end of blocks") );
	for( TInt i = 0; i < iBlockCount; i++ )
		{
		CreateTestData( i, ETrue );
		}

	//
	// Do a full scatter-gather test
	//
	TestScatterGather();

	TestReadAcrossBlock();
	}





TInt E32Main()
	{
	test.Title();
	test.Start(_L("Testing media read operations"));

	CReadTest reader;
	TRAPD( ret, reader.CreateL() );
	test( KErrNone == ret );
	reader.DoTest();
	test.End();

	return 0;
	}