// Copyright (c) 2006-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:
// e32test\mmu\t_kblockmap.cpp
// Unit tests for TKernBlockMap
// 002 Test Initialise error checking
// 003 Test processing of user-side block map into extent list
// 004 Test processing of user-side block map into extent list, block size > read unit size
// 005 Test Read error checking
// 006 Test Read
// 
//

//! @SYMTestCaseID			KBASE-T_KBLOCKMAP-0338
//! @SYMTestType			UT
//! @SYMPREQ				PREQ1110
//! @SYMTestCaseDesc		Demand Paging Kernel Blockmap tests
//! @SYMTestActions			001 Unit tests the TKernBlockMap class
//! @SYMTestExpectedResults All tests should pass.
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented

#include <e32test.h>
#include <e32debug.h>
#include <memmodel/epoc/mmubase/kblockmap.h>

RTest test(_L("T_KBLOCKMAP"));

#define test_noError(x) { TInt _r = (x); if (_r < 0) HandleError(_r, __LINE__); }
#define test_notNull(x) { TAny* _a = (TAny*)(x); if (_a == NULL) HandleNull(__LINE__); }
#define test_equal(e, a) { TInt _e = (e); TInt _a = (a); if (_e != _a) HandleNotEqual(_e, _a, __LINE__); }

void HandleError(TInt aError, TInt aLine)
	{
	test.Printf(_L("Error %d\n"), aError);
	test.operator()(EFalse, aLine);
	}

void HandleNull(TInt aLine)
	{
	test.Printf(_L("Null value\n"));
	test.operator()(EFalse, aLine);
	}

void HandleNotEqual(TInt aExpected, TInt aActual, TInt aLine)
	{
	test.Printf(_L("Expected 0x%x but got 0x%x\n"), aExpected, aActual);
	test.operator()(EFalse, aLine);
	}

/// An list of "pod" objects which can be initialised by passing a variable number of TUints to its
/// constructor
template <class T>
class CList
	{
public:
	CList();
	CList(TInt aCount, ...);
	virtual ~CList();
	inline TInt Count() const { return iCount; }
	const T* Entries() const { return iEntries; }
	const T& operator[](TInt aIndex) const;
protected:
	void Set(TInt aCount, VA_LIST aList);
private:
	const CList<T>& operator=(const CList<T>&);
	TInt iCount;
	T* iEntries;
	};

template <class T>
CList<T>::CList()
	: iCount(0), iEntries(NULL)
	{
	__ASSERT_COMPILE(sizeof(T) % sizeof(TUint32) == 0);
	}

template <class T>
CList<T>::CList(TInt aCount, ...)
	: iCount(aCount)
	{
	VA_LIST list;
	VA_START(list, aCount);
	Set(aCount, list);
	}

template <class T>
CList<T>::~CList()
	{
	User::Free(iEntries);
	iEntries = NULL;
	}

template <class T>
void CList<T>::Set(TInt aCount, VA_LIST aList)
	{
	iCount = aCount;
	test(iEntries == NULL);
	iEntries = (T*)User::Alloc(sizeof(T) * iCount);
	test_notNull(iEntries);
	TInt argCount = iCount * (sizeof(T) / sizeof(TUint32));
	for (TInt i = 0 ; i < argCount ; ++i)
		((TUint32*)iEntries)[i] = VA_ARG(aList, TUint32);
	}

template <class T>
const T& CList<T>::operator[](TInt aIndex) const
	{
	test(aIndex < iCount);
	return iEntries[aIndex];
	}

/// Holds all the data associated with the user-side representation of a block map
class CBlockMap : public CList<TBlockMapEntryBase>
	{
public:
	CBlockMap(TUint aBlockGranularity,
			  TUint aBlockStartOffset,
			  TInt64 aStartBlockAddress,
			  TUint aReadUnitShift,
			  TUint aCodeLengthInFile,
			  TUint aEntriesSize,
			  ...);
	inline const SBlockMapInfoBase& Info() const { return iInfo; }
	inline TInt ReadUnitShift() const { return iReadUnitShift; }
	inline TInt CodeLengthInFile() const { return iCodeLengthInFile; }
	inline TInt EntriesSize() const { return iEntriesSize; }
private:
	SBlockMapInfoBase iInfo;
	TBlockMapEntryBase* iEntries;
	TUint iReadUnitShift;
	TUint iCodeLengthInFile;
	TUint iEntriesSize;
	};

CBlockMap::CBlockMap(TUint aBlockGranularity,
					 TUint aBlockStartOffset,
					 TInt64 aStartBlockAddress,
					 TUint aReadUnitShift,
					 TUint aCodeLengthInFile,
					 TUint aEntriesSize,
					 ...)
	{
	iInfo.iBlockGranularity = aBlockGranularity;
	iInfo.iBlockStartOffset = aBlockStartOffset;
	iInfo.iStartBlockAddress = aStartBlockAddress;
	// don't care about iInfo.iLocalDriveNumber for test purposes
	iReadUnitShift = aReadUnitShift;
	iCodeLengthInFile = aCodeLengthInFile;
	iEntriesSize = aEntriesSize;
	iEntries = (TBlockMapEntryBase*)User::Alloc(iEntriesSize);

	VA_LIST list;
	VA_START(list, aEntriesSize);
	Set(iEntriesSize / sizeof(TBlockMapEntryBase), list);
	}

/// A list of extents, for comparison with those generated by the kernel block map processing code
class CExtentList : public CList<TBlockMap::SExtent>
	{
public:
	typedef TBlockMap::SExtent SExtent;
	CExtentList(TInt aCount, ...);
	void Dump() const;
	};

CExtentList::CExtentList(TInt aCount, ...)
	{
	VA_LIST list;
	VA_START(list, aCount);
	Set(aCount, list);
	}

void CExtentList::Dump() const
	{
	RDebug::Printf("CExtentList:\n");
	const CExtentList& self = *this;
	for (TInt i = 0 ; i < Count() ; ++i)
		RDebug::Printf("  %d: %08x -> %08x: %08x\n", i, self[i].iDataOffset, self[i+1].iDataOffset, self[i].iBlockNumber);
	}

TBool operator==(const TBlockMap::SExtent& a, const TBlockMap::SExtent& b)
	{
	return a.iDataOffset == b.iDataOffset && a.iBlockNumber == b.iBlockNumber;
	}

TBool operator!=(const TBlockMap::SExtent& a, const TBlockMap::SExtent& b)
	{
	return !(a == b);
	}

TBool CompareExtentsEqual(const TBlockMap& aBlockMap, const CExtentList& aExtentList)
	{
	if (aBlockMap.Count() != aExtentList.Count())
		return EFalse;
	for (TInt i = 0 ; i < aBlockMap.Count() ; ++i)
		{
		if (aBlockMap.Extent(i) != aExtentList[i])
			return EFalse;
		}
	return ETrue;
	}

TInt MakeKernBlockMap(TBlockMap& aKbm, const CBlockMap& aUbm)
	{
	TBlockMapEntryBase* buffer = (TBlockMapEntryBase*)User::Alloc(aUbm.EntriesSize());
	test_notNull(buffer);
	Mem::Copy(buffer, aUbm.Entries(), aUbm.EntriesSize());
	return aKbm.Initialise(aUbm.Info(),
						   buffer,
						   aUbm.EntriesSize(),
						   aUbm.ReadUnitShift(),
						   aUbm.CodeLengthInFile());
}

void MakeKernBlockMapAndTestExtents(const CBlockMap& aUbm, const CExtentList& aExpectedExtentList)
	{
	TBlockMap kbm;
	test_noError(MakeKernBlockMap(kbm, aUbm));
	TBool equal = CompareExtentsEqual(kbm, aExpectedExtentList);
	if (!equal)
		{
		aExpectedExtentList.Dump();
#ifdef _DEBUG
		kbm.Dump();
#endif
		}
	test(equal);
	}

// Tests

void TestInitialiseErrors()
	{
	test.Next(_L("Test Initialise error checking"));
	TBlockMap kbm;

	// Block size must be a power of two
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(513, 0, 0, 9, 1, 8, 1, 0)));

	// Block size must be greater than or equal to the read unit size
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 10, 1, 8, 1, 0)));

	// Block start offset must be less than or equal to block size
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 513, 0, 9, 1, 8, 1, 0)));

	// Block zero address must be a multiple of the block size
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 0, 1, 9, 1, 8, 1, 0)));

	// Code length in file must be greater than zero
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 9, 0, 8, 1, 0)));

	// Size of entries array must be multiple of the size of one entry
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 9, 1, 9, 1, 0)));

	// Size of entries must be non-zero
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 9, 1, 0)));
	
	// Size of block map must be greater or equal to size of data in file
	test_equal(KErrArgument, MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 9, 513, 8, 1, 0)));
	}

	// blockGranularity, startOffset, blockZeroAddress, readUnitShift, codeLengthInFile, entriesSize, (numberOfBlocks, startBlock)+

void TestExtentList()
	{
	test.Next(_L("Test processing of user-side block map into extent list"));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |=      |       |       |       |       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 0, 0, 9, 1, 8, 1, 0),
								   CExtentList(1, 0, 0));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |=      |       |       |       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 0, 512, 9, 1, 8, 1, 0),
								   CExtentList(1, 0, 1));
	
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       | =     |       |       |       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 1, 8, 1, 0),
								   CExtentList(1, -23, 1));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |       |       | =     |       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 1, 8, 1, 2),
								   CExtentList(1, -23, 3));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |       |       | ======|       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 489, 8, 1, 2),
								   CExtentList(1, -23, 3));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |       |       | ========      |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 600, 8, 2, 2),
								   CExtentList(1, -23, 3));
	
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |       |       | ==============|       |==     |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 1100, 16, 2, 2, 1, 5),
								   CExtentList(2, -23, 3, 1001, 6));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |       |       | ==============|       |=======|       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 1513, 16, 2, 2, 1, 5),
								   CExtentList(2, -23, 3, 1001, 6));
	
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |       |       |       | ==============|       |=======|=      |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 9, 1540, 16, 2, 2, 2, 5),
								   CExtentList(2, -23, 3, 1001, 6));
	}

void TestExtentListScaled()
	{
	test.Next(_L("Test processing of user-side block map into extent list, block size > read unit size"));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |=  :   |   :   |   :   |   :   |   :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 0, 0, 8, 1, 8, 1, 0),
								   CExtentList(1, 0, 0));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |=  :   |   :   |   :   |   :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 0, 512, 8, 1, 8, 1, 0),
								   CExtentList(1, 0, 2));
	
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   | = :   |   :   |   :   |   :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 23, 512, 8, 1, 8, 1, 0),
								   CExtentList(1, -23, 2));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   : = |   :   |   :   |   :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 1, 8, 1, 0),
								   CExtentList(1, -24, 3));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : = |   :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 1, 8, 1, 2),
								   CExtentList(1, -24, 7));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ==|   :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 232, 8, 1, 2),
								   CExtentList(1, -24, 7));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ====  :   |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 333, 8, 2, 2),
								   CExtentList(1, -24, 7));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ========  |   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 666, 8, 2, 2),
								   CExtentList(1, -24, 7));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ==========|   :   |   :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 744, 8, 2, 2),
								   CExtentList(1, -24, 7));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ==========|   :   |== :   |   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 888, 16, 2, 2, 1, 5),
								   CExtentList(2, -24, 7, 744, 12));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ==========|   :   |=======|   :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 1256, 16, 2, 2, 1, 5),
								   CExtentList(2, -24, 7, 744, 12));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ==========|   :   |=========  :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	MakeKernBlockMapAndTestExtents(CBlockMap(512, 280, 512, 8, 1350, 16, 2, 2, 2, 5),
								   CExtentList(2, -24, 7, 744, 12));
	}

/// Holds the expected arguments for one call to the read function
struct SReadEntry
	{
	TLinAddr iBuffer;
	TInt iBlockNumber;
	TInt iBlockCount;
	};

/// Holds a list of expected arguments for all calls to the read function
class CReadInfo : public CList<SReadEntry>
	{
public:
	CReadInfo(TInt aReturnVal, TInt aCount, ...);
	const SReadEntry& Next();
	void Done() const;
	TInt ReturnVal() const { return iReturnVal; }
private:
	TInt iPos;
	TInt iReturnVal;
	};

CReadInfo::CReadInfo(TInt aReturnVal, TInt aCount, ...)
	: iPos(0), iReturnVal(aReturnVal)
	{
	VA_LIST list;
	VA_START(list, aCount);
	Set(aCount, list);
	}

const SReadEntry& CReadInfo::Next()
	{
	const CList<SReadEntry>& self = *this;
	return self[iPos++];
	}

void CReadInfo::Done() const
	{
	test_equal(Count(), iPos);
	}

TInt ReadFunc(TAny* aArg, TAny*, TLinAddr aBuffer, TInt aBlockNumber, TInt aBlockCount)
	{
	CReadInfo& info = *(CReadInfo*)aArg;
	const SReadEntry& expected = info.Next();
	test_equal(expected.iBuffer, aBuffer);
	test_equal(expected.iBlockNumber, aBlockNumber);
	test_equal(expected.iBlockCount, aBlockCount);
	return KErrNone;
	}

void TestRead(const TBlockMap& aBlockMap,
			  TLinAddr aBuffer,
			  TInt aPos,
			  TInt aLength,
			  TInt aReadUnitShift,
			  const CReadInfo& aExpected)
	{
	test_equal(aExpected.ReturnVal(),
			   aBlockMap.Read(aBuffer, aPos, aLength, aReadUnitShift, ReadFunc, (TAny*)&aExpected, (TAny*)NULL));
	aExpected.Done();
	}

void TestReadErrors()
	{
	test.Next(_L("Test Read error checking"));

	TBlockMap kbm, kbm2;
	
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |=      |       |       |       |       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	test_noError(MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 9, 1, 8, 1, 0)));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   :   |   :   |   :   |   : ==========|   :   |=========  :   |   :   |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------	
	test_noError(MakeKernBlockMap(kbm2, CBlockMap(512, 280, 512, 8, 1350, 16, 2, 2, 2, 5)));

	// Test read start position is outside block map
	TestRead(kbm, 0, 1, 1, 9, CReadInfo(KErrArgument, 0));
	TestRead(kbm2, 0, 1350, 1, 8, CReadInfo(KErrArgument, 0));

	// Test read start position is negative
	TestRead(kbm, 0, -1, 1, 9, CReadInfo(KErrArgument, 0));
	TestRead(kbm2, 0, -1, 1, 8, CReadInfo(KErrArgument, 0));

	// Test read length exceeds block map length 
	TestRead(kbm, 0, 0, 2, 9, CReadInfo(KErrArgument, 1, 0, 0, 1));
	TestRead(kbm2, 0, 1349, 2, 8, CReadInfo(KErrArgument, 1, 0, 14, 1));
	}

void TestReads()
	{
	test.Next(_L("Test Read"));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |=======================|       |       |       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	
	TBlockMap kbm2;
	test_noError(MakeKernBlockMap(kbm2, CBlockMap(512, 0, 0, 9, 1536, 8, 3, 0)));

	// Test correct number of blocks read
	TestRead(kbm2, 0, 0, 512, 9, CReadInfo(0, 1, 0, 0, 1));
	TestRead(kbm2, 0, 0, 513, 9, CReadInfo(0, 1, 0, 0, 2));
	TestRead(kbm2, 0, 0, 1024, 9, CReadInfo(0, 1, 0, 0, 2));
	TestRead(kbm2, 0, 0, 1025, 9, CReadInfo(0, 1, 0, 0, 3));
	TestRead(kbm2, 0, 0, 1536, 9, CReadInfo(0, 1, 0, 0, 3));

	// Test start offset not aligned to read unit
	TestRead(kbm2, 0, 1, 511, 9, CReadInfo(1, 1, 0, 0, 1));
	TestRead(kbm2, 0, 256, 256, 9, CReadInfo(256, 1, 0, 0, 1));
	TestRead(kbm2, 0, 511, 1, 9, CReadInfo(511, 1, 0, 0, 1));
	TestRead(kbm2, 0, 513, 511, 9, CReadInfo(1, 1, 0, 1, 1));
	TestRead(kbm2, 0, 1023, 1, 9, CReadInfo(511, 1, 0, 1, 1));

	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |=======|       |=======|       |=======|       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	
	TBlockMap kbm;
	test_noError(MakeKernBlockMap(kbm, CBlockMap(512, 0, 0, 9, 1536, 24, 1, 0, 1, 3, 1, 5)));

	// Test correct block selected for read
	TestRead(kbm, 0, 0, 1, 9, CReadInfo(0, 1, 0, 0, 1));
	TestRead(kbm, 0, 256, 1, 9, CReadInfo(256, 1, 0, 0, 1));
	TestRead(kbm, 0, 511, 1, 9, CReadInfo(511, 1, 0, 0, 1));
	TestRead(kbm, 0, 512, 1, 9, CReadInfo(0, 1, 0, 3, 1));
	TestRead(kbm, 0, 768, 1, 9, CReadInfo(256, 1, 0, 3, 1));
	TestRead(kbm, 0, 1023, 1, 9, CReadInfo(511, 1, 0, 3, 1));
	TestRead(kbm, 0, 1535, 1, 9, CReadInfo(511, 1, 0, 5, 1));

	// Test reading multiple blocks
	TestRead(kbm, 0, 0, 513, 9, CReadInfo(0, 2, 0, 0, 1, 512, 3, 1));
	TestRead(kbm, 0, 0, 1024, 9, CReadInfo(0, 2, 0, 0, 1, 512, 3, 1));
	TestRead(kbm, 0, 0, 1025, 9, CReadInfo(0, 3, 0, 0, 1, 512, 3, 1, 1024, 5, 1));
	TestRead(kbm, 0, 0, 1536, 9, CReadInfo(0, 3, 0, 0, 1, 512, 3, 1, 1024, 5, 1));


	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	//  |   ====|       |=======|       |=======|       |       |       |       |
	//  +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------
	
	TBlockMap kbm3;
	test_noError(MakeKernBlockMap(kbm3, CBlockMap(512, 256, 0, 9, 1280, 24, 1, 0, 1, 3, 1, 5)));

	// Test correct block selected for read
	TestRead(kbm3, 0, 0, 1, 9, CReadInfo(256, 1, 0, 0, 1));
	TestRead(kbm3, 0, 255, 1, 9, CReadInfo(511, 1, 0, 0, 1));
	TestRead(kbm3, 0, 256, 1, 9, CReadInfo(0, 1, 0, 3, 1));
	TestRead(kbm3, 0, 767, 1, 9, CReadInfo(511, 1, 0, 3, 1));
	TestRead(kbm3, 0, 768, 1, 9, CReadInfo(0, 1, 0, 5, 1));

	// Test reading multiple blocks
	TestRead(kbm3, 0, 0, 256, 9, CReadInfo(256, 1, 0, 0, 1));
	TestRead(kbm3, 0, 0, 257, 9, CReadInfo(256, 2, 0, 0, 1, 512, 3, 1));
	TestRead(kbm3, 0, 0, 768, 9, CReadInfo(256, 2, 0, 0, 1, 512, 3, 1));
	TestRead(kbm3, 0, 0, 769, 9, CReadInfo(256, 3, 0, 0, 1, 512, 3, 1, 1024, 5, 1));
	TestRead(kbm3, 0, 0, 1280, 9, CReadInfo(256, 3, 0, 0, 1, 512, 3, 1, 1024, 5, 1));
	}

TInt E32Main()
	{
	test.Title();
	test.Start(_L("Unit tests the TKernBlockMap class"));

	TestInitialiseErrors();
	TestExtentList();
	TestExtentListScaled();
	TestReadErrors();
	TestReads();
 
	test.End();

	return KErrNone;
	}
