kerneltest/e32test/lffs/tf_write.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2001-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"
#include "tf_write.h"

_LIT( KTestName, "TF_WRITE" );
RTest test( KTestName );


const TInt64 KRandomSeed1(MAKE_TINT64(0x3e000111,0xAFCBDF0F));


GLDEF_C void Panic( TPanicNo aPanic )
	{
	User::Panic( KTestName, aPanic );
	}


// **********************************************************************
// Implementation of the writer classes

TWriteBase::TWriteBase( CWriteTest& aOwner )
	: iOwner( aOwner )
	{
	}

void TWriteBase::CheckedWrite(TInt aPos,const TDesC8& aSrc)
	{
	Write( aPos, aSrc );
	test( iOwner.CompareAgainstFlash( aPos, aSrc ) );
	}


TSimpleWrite::TSimpleWrite( CWriteTest& aOwner )
	: TWriteBase( aOwner ), iDrive( aOwner.Drive() )
	{
	}

void TSimpleWrite::Write(TInt aPos,const TDesC8& aSrc)
	{
	TInt64	pos( aPos );
//	test( KErrNone == iDrive.Write( pos, aSrc ) );
	TInt rv = iDrive.Write( pos, aSrc );
	if( KErrNone != rv )
		{
		test.Printf( _L("TBusLocalDrive::Write returned %d"), rv );
		test( EFalse );
		}
	}


	
TThreadWrite::TThreadWrite( CWriteTest& aOwner )
	: TWriteBase( aOwner ), iDrive( aOwner.Drive() ),
	iThreadHandle( aOwner.DummyThreadHandle() )
	{
	}

void TThreadWrite::Write(TInt aPos,const TDesC8& aSrc)
	{
	TInt64	pos( aPos );
#if 0
	test( KErrNone == iDrive.Write( pos, aSrc.Length(), &aSrc, iThreadHandle, 0 ) );
#else
	test( KErrNone == iDrive.Write( pos, aSrc.Length(), &aSrc, KLocalMessageHandle, 0 ) );
#endif
	}

		
void TThreadWrite::CheckedThreadWrite(TInt aPos, TInt aLength, const TDesC8& aSrc, TInt aDescOffset )
	{
	TInt64	pos( aPos );
#if 0
	test( KErrNone == iDrive.Write( pos, aLength, &aSrc, iThreadHandle, aDescOffset ) );
#else
	test( KErrNone == iDrive.Write( pos, aLength, &aSrc, KLocalMessageHandle, aDescOffset ) );
#endif
	test( iOwner.CompareAgainstFlash( aPos, aLength, aSrc, aDescOffset ) );
	}

void TThreadWrite::CurrentThreadCheckedThreadWrite(TInt aPos, TInt aLength, const TDesC8& aSrc, TInt aDescOffset )
	{
	TInt64	pos( aPos );
#if 0
	test( KErrNone == iDrive.Write( pos, aLength, &aSrc, RThread().Handle(), aDescOffset ) );
#else
	test( KErrNone == iDrive.Write( pos, aLength, &aSrc, KLocalMessageHandle, aDescOffset ) );
#endif
	test( iOwner.CompareAgainstFlash( aPos, aLength, aSrc, aDescOffset ) );
	}

// **********************************************************************
// Implementation of CBlockManager

CBlockManager::CBlockManager( TBusLocalDrive& aDrive, CWriteTest& aOwner )
	: iDrive( aDrive ), iOwner( aOwner )
	{
	}

CBlockManager::~CBlockManager()
	{
	delete[] iEraseArray;
	}

void CBlockManager::CreateL()
	{
	//
	// Get size of Flash drive
	//
	test.Printf( _L("Reading block info...") );
	TLocalDriveCapsV2Buf info;
    iDrive.Caps(info);
	TUint flashSize = I64LOW(info().iSize);
	test( 0 == I64HIGH(info().iSize));
	iBlockSize = info().iEraseBlockSize;
	test( 0 == (iBlockSize & 3) );
	iBlockCount = flashSize / iBlockSize;
	test( 0 != iBlockCount );

	test.Printf( _L("Flash block size=0x%x; block count=%d\n"), iBlockSize, iBlockCount );
	
	iEraseArray = new(ELeave) TEraseStatus[iBlockCount];
	test.Printf( _L("Erase status array created") );
	}


void CBlockManager::EraseBlock( TInt aBlockNumber )
	{
	__ASSERT_ALWAYS( aBlockNumber < iBlockCount, Panic( EPanicEraseBlockOOR ) );
	__ASSERT_ALWAYS( aBlockNumber < iBlockCount, Panic( EPanicEraseBlockNeg ) );
	_LIT( KEraseMsg, "Erasing block %d" );
	test.Printf( KEraseMsg, aBlockNumber );
	test( KErrNone == iDrive.Format( BlockAddress( aBlockNumber ), iBlockSize ) );
	VerifyErased( aBlockNumber );
	iEraseArray[ aBlockNumber ] = EErased;
	}

void CBlockManager::EraseAllBlocks()
	{
	_LIT( KEraseMsg, "Erasing all blocks" );
	test.Printf( KEraseMsg );
	for( TInt i = 0; i < iBlockCount; i++ )
		{
		EraseBlock( i );
		}
	}

void CBlockManager::VerifyErased( TInt aBlockNumber )
	{
	TUint offset = aBlockNumber * iBlockSize;
	
	TBool failed = EFalse;
	const TInt readBufLen = iReadBuffer.MaxLength();

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

		const TUint32* p = (const TUint32*)iReadBuffer.Ptr();
		for( TInt i = 0; i < readBufLen; i += 4 )
			{
			if( 0xFFFFFFFF != *p )
				{
				failed = ETrue;
				test.Printf( _L("... FAILED: byte @ offs=0x%x, read=0x%x, expected=0xFF\n"), 
								offset+i, p[0] );
				test(EFalse);
				}
			++p;
			}
		offset += readBufLen;
		remaining -= readBufLen;
		}
	}


void CBlockManager::InitialiseSequentialBlockAllocator()
	//
	// Clears the erase status and resets to block zero
	//
	{
	for( TInt i = 0; i < iBlockCount; i++ )
		{
		iEraseArray[i] = ENotErased;
		}
	iNextBlock = 0;
	}


TInt CBlockManager::NextErasedBlock()
	{
	if( iNextBlock >= iBlockCount )
		{
		iNextBlock = 0;
		}

	if( ENotErased == iEraseArray[iNextBlock] )
		{
		EraseBlock( iNextBlock );
		}
	iEraseArray[iNextBlock] = ENotErased;	// assume it is going to be used
	return iNextBlock;
	}

void CBlockManager::InitialiseDataChunkAllocator()
	{
	iDataBlock = NextErasedBlock();
	iDataOffset = 0;
	}


TUint CBlockManager::NextErasedDataChunk( TInt aRequiredLength, TInt aMultiple )
	//
	// Request a chunk of erased flash of size aRequiredLength bytes on a
	// boundary of aMultiple bytes. E,g, to allocate a buffer on 12 bytes length
	// on a 32-byte boundary, aRequiredLength = 12, aMultiple=12
	//
	// The byte count is rounded up to a multiple of 4 bytes
	//
	{
	aRequiredLength = (aRequiredLength + 3) & ~0x3;
	
	TUint chunkBase = ((iDataOffset + aMultiple - 1) / aMultiple) * aMultiple;
	if( chunkBase > (TUint)iBlockSize || chunkBase + aRequiredLength > (TUint)iBlockSize )
		{
		iDataBlock = NextErasedBlock();
		chunkBase = 0;
		}
	
	iDataOffset = ( chunkBase + aRequiredLength + 3) & ~0x3;

	return BlockAddress( iDataBlock ) + chunkBase;
	}



inline TInt CBlockManager::BlockCount() const
	{
	return iBlockCount;
	}

inline TInt CBlockManager::BlockSize() const
	{
	return iBlockSize;
	}

inline TInt CBlockManager::FlashSize() const
	{
	return iBlockSize * iBlockCount;
	}

inline TUint CBlockManager::BlockAddress( TInt aBlockNumber ) const
	{
	return (TUint)aBlockNumber * (TUint)iBlockSize;
	}


// **********************************************************************
// Implementation of CWriteTest

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

	delete iBlocks;
	delete iSimpleWriter;
	delete iThreadWriter;
	}


void CWriteTest::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 - not 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;

	//
	// Initialise the block manager
	//
	iBlocks = new(ELeave) CBlockManager( iDrive, *this );
	iBlocks->CreateL();

	//
	// 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
	test( KErrNone == iDummyThread.Create( _L("DUMMY"), DummyThread, KDefaultStackSize, KMinHeapSize, KMinHeapSize, NULL ) );
#endif
	test.Printf( _L("Main thread handle=%d; dummy thread handle=%d"),
		RThread().Handle(), DummyThreadHandle() );

	//
	// Create the writer classes
	//
	iSimpleWriter = new(ELeave) TSimpleWrite( *this );
	iThreadWriter = new(ELeave) TThreadWrite( *this );
	
	//
	// Seed the pseudo-random number generator
	//
	iRandom.SetSeed( KRandomSeed1 );


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


TInt CWriteTest::DummyThread( TAny* /* aParam */ )
	//
	// Thread does nothing at all
	//
	{
	for(;;)
		{
		User::WaitForAnyRequest();	// just block
		}
	}

void CWriteTest::CreateRandomData( TDes8& aDestBuf, TInt aLength )
	//
	// Fills supplied descriptor with aLength bytes of pseudo-random test data
	//
	{
	aDestBuf.SetLength( aLength );
	TUint32* p = (TUint32*)aDestBuf.Ptr();
	for( TInt j = aLength/4; j > 0 ; j-- )
		{
		*p++ = iRandom.Next();
		}
	
	if( aLength & 0x3 )
		{
		TUint8* q = (TUint8*)p;
		for( TInt k = aLength & 3; k > 0; k-- )
			{
			*q++ = (TUint8)iRandom.Next();
			}
		}
	}


TBool CWriteTest::CheckOnes( TUint aFlashOffset, TInt aLength )
	//
	// Checks that aLength bytes of data from offset aFlashOffset
	// all contain 0xFF
	//
	{
	TUint offset = aFlashOffset;
	
	TBool failed = EFalse;
	const TInt readBufLen = iReadBuffer.MaxLength();

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

		const TUint8* p = iReadBuffer.Ptr();
		for( TInt i = 0; i < readLen; ++i )
			{
			if( 0xFF != *p )
				{
				failed = ETrue;
				test.Printf( _L("... FAILED: byte @ offs=0x%x, read=0x%x, expected=0xFF\n"), 
								offset+i, p[0] );
				break;
				}
			++p;
			}
		offset += readLen;
		remaining -= readLen;
		}
	
	return !failed;
	}


TBool CWriteTest::CompareAgainstFlash( TInt aFlashOffset, TInt aLength, const TDesC8& 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. aLength bytes
	// are tested.
	//
	{
	__ASSERT_ALWAYS( aDescOffset + aLength <= aDes.Length(), Panic( EPanicCompareDescOverflow ) );
	TInt dataLength = aLength;
	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;
	}

TBool CWriteTest::CompareAgainstFlash( TInt aFlashOffset, const TDesC8& aDes )
	//
	// Checks that the data in aDes matches that in the Flash at position
	// aFlashOffset.
	// aDes->Length() bytes are tested.
	//
	{
	return CompareAgainstFlash( aFlashOffset, aDes.Length(), aDes, 0 );
	}


void CWriteTest::SimpleWriteTest()
	{
	test.Next( _L("Simple write test, simple write function") );
	DoSimpleWriteTest( *iSimpleWriter );
	}

void CWriteTest::SimpleThreadWriteTest()
	{
	test.Next( _L("Simple write test, thread write function") );
	DoSimpleWriteTest( *iThreadWriter );
	}


void CWriteTest::DoSimpleWriteTest( MGeneralizedWrite& aWriter )
	//
	// Writes some random test data to the start of a block, checks that
	// it is written correctly and that the source data isn't modified
	//
	{
	TInt blockNo = iBlocks->NextErasedBlock();
	TUint blockBase = iBlocks->BlockAddress( blockNo );

	TBuf8<512> randomData;
	CreateRandomData( randomData, randomData.MaxLength() );

	TBuf8<512> randomDataDuplicate;
	randomDataDuplicate.Copy( randomData );
	test( randomDataDuplicate == randomData );

	TBuf8<sizeof(TPtr)> ptrCopy;	// used to take copies of descriptors

	//
	// Write using a constant descriptor TPtrC
	//
	test.Printf( _L("Write using TPtrC") );
	TPtrC8 ptrC( randomData );
	ptrCopy.Copy( (TUint8*)&ptrC, sizeof(ptrC) );

	aWriter.CheckedWrite( blockBase + 0, ptrC );

	test.Printf( _L("Check descriptor not modified by write function") );
	test( 0 == Mem::Compare( (TUint8*)&ptrC, sizeof(ptrC), ptrCopy.Ptr(), sizeof(ptrC) ) );

	test.Printf( _L("Check data not modified by write function") );
	test( randomDataDuplicate == randomData );

	//
	// Write using a modifiable descriptor TPtr
	//
	test.Printf( _L("Write using TPtr") );
	TPtr8 ptr( (TUint8*)randomData.Ptr(), randomData.Length(), randomData.Length() );
	ptrCopy.Copy( (TUint8*)&ptr, sizeof(ptr) );
	
	aWriter.CheckedWrite( blockBase + 1024, ptr );

	test.Printf( _L("Check descriptor not modified by write function") );
	test( 0 == Mem::Compare( (TUint8*)&ptr, sizeof(ptr), ptrCopy.Ptr(), sizeof(ptr) ) );

	test.Printf( _L("Check data not modified by write function") );
	test( randomDataDuplicate == randomData );

	//
	// Write using a modifiable descriptor TBuf
	//
	test.Printf( _L("Write using TBuf") );
	
	aWriter.CheckedWrite( blockBase + 2048, randomData );

	test.Printf( _L("Check descriptor not modified by write function") );
	test( ptrC.Ptr() == randomData.Ptr() );
	test( 512 == randomData.Length() );
	test( 512 == randomData.MaxLength() );

	test.Printf( _L("Check data not modified by write function") );
	test( randomDataDuplicate == randomData );

	//
	// Read the data back and check it matches
	//
	test.Printf( _L("Reading data back with TBusLocalDrive::Read") );
	test( KErrNone == iDrive.Read( blockBase + 0, 512, randomDataDuplicate ) );
	test( randomDataDuplicate == randomData );
	test( KErrNone == iDrive.Read( blockBase + 1024, 512, randomDataDuplicate ) );
	test( randomDataDuplicate == randomData );
	test( KErrNone == iDrive.Read( blockBase + 2048, 512, randomDataDuplicate ) );
	test( randomDataDuplicate == randomData );
	}



void CWriteTest::AlignedWriteTest()
	{
	test.Next( _L("Aligned write test, simple write function") );
	DoAlignedWriteTest( *iSimpleWriter );
	}

void CWriteTest::AlignedThreadWriteTest()
	{
	test.Next( _L("Aligned write test, thread write function") );
	DoAlignedWriteTest( *iThreadWriter );
	}


void CWriteTest::DoAlignedWriteTest( MGeneralizedWrite& aWriter )
	//
	// Writes data of various lengths to word-aligned addresses
	//
	{
	iBlocks->InitialiseDataChunkAllocator();

	TBuf8<512> data;	

	_LIT( KWriteMsg, "  writing %d bytes @0x%x" );

	test.Printf( _L("Testing small writes") );

	for( TInt length = 1; length < 16; length++ )
		{
		CreateRandomData( data, length );
		
		// get a 32-byte data chunk on a word boundary
		TUint offset = iBlocks->NextErasedDataChunk( 32, 4 );

		test.Printf( KWriteMsg, length, offset );
		aWriter.CheckedWrite( offset, data );
		// check that the section after the data still contains all ones
		test( CheckOnes( offset + length, 32 - length ) );
		}


	test.Printf( _L("Testing large writes") );
	for( TInt length = 512-32; length <= 512 ; length++ )
		{
		CreateRandomData( data, length );
		
		// get a 544-byte data chunk on a word boundary
		TUint offset = iBlocks->NextErasedDataChunk( 544, 4 );

		test.Printf( KWriteMsg, length, offset );
		aWriter.CheckedWrite( offset, data );

		// check that the section after the data still contains all ones
		test( CheckOnes( offset + length, 544 - length ) );
		}
	}




void CWriteTest::UnalignedWriteTest()
	{
	test.Next( _L("Unaligned write test, simple write function") );
	DoUnalignedWriteTest( *iSimpleWriter );
	}

void CWriteTest::UnalignedThreadWriteTest()
	{
	test.Next( _L("Unaligned write test, thread write function") );
	DoUnalignedWriteTest( *iThreadWriter );
	}


void CWriteTest::DoUnalignedWriteTest( MGeneralizedWrite& aWriter )
	//
	// Tests writing to unaligned addresses. "Unaligned" here means
	// addresses that are not on a word boundary.
	//
	{
	TBuf8<32> data;

	_LIT( KWriteMsg, "  writing 32 bytes @0x%x" );


	for( TInt offset = 1; offset < 32; offset++ )
		{
		CreateRandomData( data, data.MaxLength() );
		
		//
		// get a 64-byte data chunk on a 256-byte boundary, then
		// start the write at <offset> bytes into this buffer
		//
		TUint dataChunk = iBlocks->NextErasedDataChunk( 64, 256 );

		test.Printf( KWriteMsg, dataChunk + offset );
		aWriter.CheckedWrite( dataChunk + offset, data );

		_LIT( KBeforeMsg,  " checking unused portion before data" );
		test.Printf( KBeforeMsg );
		test( CheckOnes( dataChunk, offset ) );

		// check that the section after the data still contains all ones
		_LIT( KAfterMsg, " checking unused portion after data" );
		test.Printf( KAfterMsg );
		test( CheckOnes( dataChunk + offset + data.Length(), 64 - offset - data.Length() ) );
		}
	}



void CWriteTest::OffsetDescriptorAlignedWriteTest()
	//
	// Tests writing using an offset into the source data buffer. Writes
	// are done to word-aligned destination addresses.
	//
	{
	test.Next( _L("Offset-desc write test, aligned dest address") );

	TBuf8<64> data;

	_LIT( KWriteMsg, "  writing 32 bytes from offset %d to @0x%x" );

//	CreateRandomData( data, data.MaxLength() );
	data.SetLength(64);
	for( TInt i = 0; i < 64; i++ )
		{
		data[i] = i;
		}

	for( TInt descOffset = 1; descOffset < 32; descOffset++ )
		{
		//
		// Get a 32-byte data chunk on a word boundary.
		//
		TUint dataChunk = iBlocks->NextErasedDataChunk( 32, 4 );

		test.Printf( KWriteMsg, descOffset, dataChunk );
		iThreadWriter->CheckedThreadWrite( dataChunk, 32, data, descOffset );

		//
		// Read the data back out and check it matches
		//
		_LIT( KReadBackMsg, "Reading back data" );
		test.Printf( KReadBackMsg );
		TBuf8<32> readData;
		iDrive.Read( dataChunk, 32, readData );
		TPtrC8 ptr( data.Ptr() + descOffset, 32 );
		test( ptr == readData );
		}
	}


void CWriteTest::OffsetDescriptorUnalignedWriteTest()
	//
	// This is a variation of OffsetDescriptorAlignedWriteTest that
	// also writes to non-word-aligned destionation addresses.
	//
	{
	test.Next( _L("Offset-desc write test, unaligned dest address") );

	TBuf8<64> data;

	_LIT( KWriteMsg, "  writing 32 bytes from offset %d to @0x%x" );

	CreateRandomData( data, data.MaxLength() );

	for( TInt descOffset = 1; descOffset < 32; descOffset++ )
		{
		for( TInt unalign = 1; unalign < 4; unalign++ )
			{
			//
			// Get a 40-byte data chunk on a word boundary.
			//
			TUint dataChunk = iBlocks->NextErasedDataChunk( 40, 4 );
			TUint destOffset = dataChunk + unalign;

			test.Printf( KWriteMsg, descOffset, destOffset );
			iThreadWriter->CheckedThreadWrite( destOffset, 32, data, descOffset );

			//
			// Read the data back out and check it matches
			//
			_LIT( KReadBackMsg, "Reading back data" );
			test.Printf( KReadBackMsg );
			TBuf8<32> readData;
			iDrive.Read( destOffset, 32, readData );
			TPtrC8 ptr( data.Ptr() + descOffset, 32 );
			test( ptr == readData );
			}
		}
	}


void CWriteTest::OffsetDescriptorCurrentThreadAlignedWriteTest()
	//
	// Tests writing using an offset into the source data buffer. Writes
	// are done to word-aligned destination addresses. This uses the
	// thread variant of the write function but passes the handle
	// of this thread.
	//
	{
	test.Next( _L("Offset-desc write test, current thread, aligned dest address") );

	TBuf8<64> data;

	_LIT( KWriteMsg, "  writing 32 bytes from offset %d to @0x%x" );

//	CreateRandomData( data, data.MaxLength() );
	data.SetLength(64);
	for( TInt i = 0; i < 64; i++ )
		{
		data[i] = i;
		}

	for( TInt descOffset = 1; descOffset < 32; descOffset++ )
		{
		//
		// Get a 32-byte data chunk on a word boundary.
		//
		TUint dataChunk = iBlocks->NextErasedDataChunk( 32, 4 );

		test.Printf( KWriteMsg, descOffset, dataChunk );
		iThreadWriter->CurrentThreadCheckedThreadWrite( dataChunk, 32, data, descOffset );

		//
		// Read the data back out and check it matches
		//
		_LIT( KReadBackMsg, "Reading back data" );
		test.Printf( KReadBackMsg );
		TBuf8<32> readData;
		iDrive.Read( dataChunk, 32, readData );
		TPtrC8 ptr( data.Ptr() + descOffset, 32 );
		test( ptr == readData );
		}
	}


void CWriteTest::OffsetDescriptorCurrentThreadUnalignedWriteTest()
	//
	// This is a variation of OffsetDescriptorCurrentThreadAlignedWriteTest
	// that also writes to non-word-aligned destionation addresses.
	//
	{
	test.Next( _L("Offset-desc write test, current thread, unaligned dest address") );

	TBuf8<64> data;

	_LIT( KWriteMsg, "  writing 32 bytes from offset %d to @0x%x" );

	CreateRandomData( data, data.MaxLength() );

	for( TInt descOffset = 1; descOffset < 32; descOffset++ )
		{
		for( TInt unalign = 1; unalign < 4; unalign++ )
			{
			//
			// Get a 40-byte data chunk on a word boundary.
			//
			TUint dataChunk = iBlocks->NextErasedDataChunk( 40, 4 );
			TUint destOffset = dataChunk + unalign;

			test.Printf( KWriteMsg, descOffset, destOffset );
			iThreadWriter->CurrentThreadCheckedThreadWrite( destOffset, 32, data, descOffset );

			//
			// Read the data back out and check it matches
			//
			_LIT( KReadBackMsg, "Reading back data" );
			test.Printf( KReadBackMsg );
			TBuf8<32> readData;
			iDrive.Read( destOffset, 32, readData );
			TPtrC8 ptr( data.Ptr() + descOffset, 32 );
			test( ptr == readData );
			}
		}
	}



void CWriteTest::JoinedWriteTest()
	//
	// Makes two consecutive writes. Checks that the complete
	// data block was written correctly. The data is written within
	// a 64-byte window and the join position is moved along to each
	// possible location
	{
	
	test.Next( _L("Joined write test, simple writes") );
	
	//
	// Reinitialise the chunk allocator
	//
	iBlocks->InitialiseDataChunkAllocator();
	
	for( TInt join = 1; join < 63; join++ )
		{
		TBuf8<64> fullData;
		CreateRandomData( fullData, fullData.MaxLength() );
		
		//
		// Create two TPtrC8s to the two parts of the data
		//
		TPtrC8 first( fullData.Ptr(), join );
		TPtrC8 second( fullData.Ptr() + join, fullData.MaxLength() - join );
		__ASSERT_ALWAYS( first.Length() + second.Length() == 64, Panic( EPanicJoinMaths ) );

		//
		// Get a location in the Flash to write to
		//
		TUint dataChunk = iBlocks->NextErasedDataChunk( 64, 64 );

		//
		// Write the two halves of the data
		//
		_LIT( KWriteMsg, "  writing %d bytes @ 0x%x and %d bytes @ 0x%x" );
		test.Printf( KWriteMsg, first.Length(), dataChunk,
								second.Length(), dataChunk + first.Length() );
		test( KErrNone == iDrive.Write( dataChunk, first ) );
		test( KErrNone == iDrive.Write( dataChunk + first.Length(), second ) );

		//
		// Compare the data
		//
		_LIT( KCompareMsg, "  comparing data against Flash" );
		test.Printf( KCompareMsg );
		test( CompareAgainstFlash( dataChunk, fullData ) );
		}
	}


void CWriteTest::JoinedThreadWriteTest()
	//
	// Makes two consecutive writes. Checks that the complete
	// data block was written correctly. The data is written within
	// a 64-byte window and the join position is moved along to each
	// possible location
	//
	// This is similar to JoinedWriteTest except that the thread write
	// function is used with a descriptor offset to chop up the
	// source data
	//
	{
	
	test.Next( _L("Joined write test, thread writes") );
	
	//
	// Reinitialise the chunk allocator
	//
	iBlocks->InitialiseDataChunkAllocator();
	
	for( TInt join = 1; join < 63; join++ )
		{
		TBuf8<64> fullData;
		CreateRandomData( fullData, fullData.MaxLength() );
		
		//
		// Get a location in the Flash to write to
		//
		TUint dataChunk = iBlocks->NextErasedDataChunk( 64, 64 );

		//
		// Write the two halves of the data
		//
		_LIT( KWriteMsg, "  writing %d bytes @ 0x%x and %d bytes @ 0x%x" );
		test.Printf( KWriteMsg, join, dataChunk, 64 - join, dataChunk + join );
#if 0
		test( KErrNone == iDrive.Write( dataChunk, join, &fullData, DummyThreadHandle(), 0 ) );
		test( KErrNone == iDrive.Write( dataChunk + join, 64-join, &fullData, DummyThreadHandle(), join ) );
#else
		test( KErrNone == iDrive.Write( dataChunk, join, &fullData, KLocalMessageHandle, 0 ) );
		test( KErrNone == iDrive.Write( dataChunk + join, 64-join, &fullData, KLocalMessageHandle, join ) );
#endif


		//
		// Compare the data
		//
		_LIT( KCompareMsg, "  comparing data against Flash" );
		test.Printf( KCompareMsg );
		test( CompareAgainstFlash( dataChunk, fullData ) );
		}
	}



void CWriteTest::SingleBitOverwriteTest()
	//
	// Tests overwriting single bits within a byte. a 32-byte
	// section of Flash is filled with data, with one byte initially
	// 0xFF. A bit is then written to zero and the whole data block
	// is verified.
	//
	{
	test.Next( _L("Single bit overwrite test") );

	iBlocks->InitialiseDataChunkAllocator();

	for( TInt testByteOffset = 0; testByteOffset < 32; testByteOffset++ )
		{
		for( TInt testBitNumber = 0; testBitNumber < 8; testBitNumber++ )
			{
			TBuf8<32> data;
			CreateRandomData( data, data.MaxLength() );
			data[ testByteOffset ] = 0xFF;	// force test byte to 0xFF

			TUint flashOffset = iBlocks->NextErasedDataChunk( 32, 32 );
			
			_LIT( KWriteMsg, "writing test data @0x%x, test byte offset=%d; test bit #%d");
			test.Printf( KWriteMsg, flashOffset, testByteOffset, testBitNumber );

			iSimpleWriter->CheckedWrite( flashOffset, data );

			// clear the test bit
			TBuf8<1> byte;
			byte.SetLength(1);
			byte[0] = ~(1 << testBitNumber);
			data[ testByteOffset ] = byte[0];
			
			iSimpleWriter->CheckedWrite( flashOffset + testByteOffset, byte );

			// check that the contents of the Flash matches the buffer
			test( CompareAgainstFlash( flashOffset, data ) );
			}
		}
	}

void CWriteTest::TwoBitOverwriteTest()
	//
	// Tests overwriting two bits within a byte. a 32-byte
	// section of Flash is filled with data, with one byte initially
	// 0xFF. Two bits are then written to zero and the whole data block
	// is verified.
	//
	{
	static const TUint pattConv[16] =
		{
		// used to create a string representation of binary value
		0x0000, 0x0001, 0x0010, 0x0011, 0x0100, 0x0101, 0x0110, 0x0111,
		0x1000, 0x1001, 0x1010, 0x1011, 0x1100, 0x1101, 0x1110, 0x1111
		};
	test.Next( _L("Two bit overwrite test") );

	for( TInt testByteOffset = 0; testByteOffset < 32; testByteOffset++ )
		{
		for( TInt testBitJ = 0; testBitJ < 7; testBitJ++ )
			{
			for( TInt testBitK = testBitJ+1; testBitK < 8; testBitK++ )
				{
				TBuf8<32> data;
				CreateRandomData( data, data.MaxLength() );
				data[ testByteOffset ] = 0xFF;	// force test byte to 0xFF

				TUint flashOffset = iBlocks->NextErasedDataChunk( 32, 32 );
				
				TUint8 testPattern = ~((1 << testBitJ) | (1 << testBitK));

				_LIT( KWriteMsg, "writing test data @0x%x, test byte offset=%d; test pattern = %04x%04x");
				test.Printf( KWriteMsg, flashOffset, testByteOffset, 
							pattConv[ testPattern >> 4 ], pattConv[ testPattern&0xF ] );

				iSimpleWriter->CheckedWrite( flashOffset, data );

				TBuf8<1> byte;
				byte.SetLength(1);
				byte[0] = testPattern;
				data[ testByteOffset ] = testPattern;
				
				iSimpleWriter->CheckedWrite( flashOffset + testByteOffset, byte );

				// check that the contents of the Flash matches the buffer
				test( CompareAgainstFlash( flashOffset, data ) );
				}
			}
		}
	}


void CWriteTest::RunSimulationTest()
	//
	// A simulation of the way the LFFS filesystem will use a Flash block
	// Alternately writes 24 bytes to bottom of block, 512 bytes to top,
	// clears a bit in the 24-byte block. Repeats until block is full.
	//
	{
	test.Next( _L("Simulation test") );

	TUint blockBase = iBlocks->BlockAddress( iBlocks->NextErasedBlock() );

	TUint lowAddress = blockBase;
	TUint highAddress = blockBase + iBlocks->BlockSize() - 512;

	TBuf8<24> lowData;
	TBuf8<512> highData;
	TPtrC8 overwritePtr( lowData.Ptr(), 1 );

	while( lowAddress + 24 < highAddress )
		{
		CreateRandomData( lowData, lowData.MaxLength() );
		CreateRandomData( highData, highData.MaxLength() );
		lowData[0] = 0xE7;	// just some non-0xFF value

		_LIT( KWriteMsg, "Writing block size 24 @ 0x%x; block size 512 @ 0x%x" );
		test.Printf( KWriteMsg, lowAddress, highAddress );

		iSimpleWriter->CheckedWrite( lowAddress, lowData );
		iSimpleWriter->Write( highAddress, highData );

		// Overwrite the byte
		lowData[0] = 0xA7;
		iSimpleWriter->Write( lowAddress, overwritePtr );

		test( CompareAgainstFlash( lowAddress, lowData ) );
		test( CompareAgainstFlash( highAddress, highData ) );

		lowAddress += lowData.Length();
		highAddress -= highData.Length();
		}
	}



void CWriteTest::DoTests()
	//
	// Main test dispatcher
	//
	{
	test.Next( _L("Erasing all blocks") );
	iBlocks->InitialiseSequentialBlockAllocator();
	iBlocks->EraseAllBlocks();

	//
	// Basic tests that we can write data correctly without corrupting
	// the source buffer
	//
	SimpleWriteTest();
	SimpleThreadWriteTest();

	//
	// Test aligned writes of various lengths
	//
	AlignedWriteTest();
	AlignedThreadWriteTest();

	//
	// Test writing to unaligned locations
	//
	UnalignedWriteTest();
	UnalignedThreadWriteTest();

	//
	// Test writes with offset into source desriptor
	//
	OffsetDescriptorCurrentThreadAlignedWriteTest();
	OffsetDescriptorCurrentThreadUnalignedWriteTest();
	OffsetDescriptorAlignedWriteTest();
	OffsetDescriptorUnalignedWriteTest();

	//
	// Test two consecutive writes
	//
	JoinedWriteTest();
	JoinedThreadWriteTest();

	//
	// Test that we can overwrite bits
	//
	SingleBitOverwriteTest();
	TwoBitOverwriteTest();

	//
	// A simulation test of LFFS usage
	//
	RunSimulationTest();
	}










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

	CWriteTest writeTest;
	TRAPD( ret, writeTest.CreateL() );
	test( KErrNone == ret );
	writeTest.DoTests();
	test.End();

	return 0;
	}