kerneltest/e32test/lffs/tf_read.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/lffs/tf_read.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1132 @@
+// 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;
+	}