kerneltest/e32test/heap/t_heap.cpp
changeset 0 a41df078684a
child 19 4a8fed1c0ef6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/heap/t_heap.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1209 @@
+// Copyright (c) 1995-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\heap\t_heap.cpp
+// Overview:
+// Tests RHeap class.
+// API Information:
+// RHeap
+// Details:
+// - Test that the expected methods are in the DLL by calling each one.
+// - Test heap auto expansion and compression by calling Alloc and Compress
+// and verifying the results are as expected.
+// - Verify the heap dump Base, Size, MinLength, Top and len values.
+// - Test the RHeap AllocSize, Alloc, AllocLen, Count and Free methods. Verify
+// results are as expected. Check heap object and confirm Invariant status.
+// - For an RHeap object, test and verify the results of: allocate some cells, 
+// free them with Reset, allocate some cells again, free them with Free, 
+// allocate some cells again, free them backwards, allocate again, free the 
+// odd cells then the even cells, allocate again, free one half then the other.
+// Check heap object and confirm Invariant status.
+// - For an RHeap object, test and verify the results of: attempt to resize a
+// block above the space available, resize the block to 0, resize positively, 
+// allocate a block, fill with data, allocate another block or two then resize
+// the original block such that it has to be moved in memory, then check the 
+// blocks' contents, test data was copied on reallocation, resize blocks and
+// verify data integrity, expand and shrink, verify data.
+// Check heap object and confirm Invariant status.
+// - For an RHeap object, test and verify the results of: Alloc some cells,
+// verify the Count, Check the object, Free some cells, verify the Count, 
+// Check and Reset the object, corrupt the heap data and reset the object.
+// - Test the leaving methods: AllocL and ReAllocL. Verify the results are as
+// expected.
+// - Test the RHeap methods: Alloc, Count, Size, Free and Close. Verify results
+// are as expected.
+// - Test sharing a chunk heap between two separate threads.  Each thread
+// accesses the shared heap in a timed loop, to ensure that some true
+// concurrency.
+// - Test sharing a chunk heap between two separate threads. Run each thread in
+// a timed loop, to ensure that some true concurrency. Each thread accesses
+// the shared heap and results are verified.  The heap size is used to verify
+// no leaks and that the largest available space is still available. The heap
+// is checked to verify that no cells remain allocated after the tests are
+// complete.
+// - Test sharing a heap between two threads.  The thread whose heap it was is
+// killed first.  Each thread accesses the shared heap and results are
+// verified.
+// Platforms/Drives/Compatibility:
+// All
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+// 
+//
+
+#include <e32test.h>
+#include <e32hal.h>
+#include <e32def.h>
+#include <e32def_private.h>
+
+// Sets data for Test6
+#define SetData(size)	pHeap->Reset();\
+						Cell1=pHeap->Alloc(size);\
+						Cell2=pHeap->Alloc(size);\
+						Cell3=pHeap->Alloc(size);\
+						for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+pHeap->AllocLen(Cell1); *pC++='x');\
+						for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+pHeap->AllocLen(Cell2); *pC++='y');\
+						for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); *pC++='z');\
+						OrigLen=pHeap->AllocLen(Cell2);			
+
+// Tests cell contents for Test6
+#define TestCells(Cell2Len)	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+pHeap->AllocLen(Cell1); test(*pC++=='x'));\
+							for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Len; test(*pC++=='y'));\
+							for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'));\
+							pHeap->Check();
+
+#ifdef __EABI__
+	IMPORT_D extern const TInt KHeapMinCellSize;
+#else
+    const TInt KHeapMinCellSize = 0;
+#endif
+
+const TInt KHeadSize = (TInt)RHeap::EAllocCellSize;
+const TInt KAlign = _FOFF(RHeap::_s_align, d);
+const TInt KMinCellLength = _ALIGN_UP((KHeapMinCellSize + Max(TInt(RHeap::EFreeCellSize),TInt(RHeap::EAllocCellSize))),KAlign) - RHeap::EAllocCellSize;
+const TInt KMinFreeSize = _ALIGN_UP((KHeapMinCellSize + Max(TInt(RHeap::EFreeCellSize),TInt(RHeap::EAllocCellSize))),KAlign);
+
+TInt PageSize;
+
+class RTestHeap : public RHeap
+	{
+public:
+	void __DbgTest(void* pRHeapDump) const;
+	};
+
+struct RHeapDump
+	{
+	TUint				iMinLength;
+	RChunk				iChunk;
+	TUint8				*iBase;
+	TUint8				*iTop;
+	RHeap::SCell		iFree;
+	};
+
+#pragma warning ( disable :4705 ) // statement has no effect
+RHeapDump OrigDump;
+#pragma warning ( default :4705 )
+
+#if defined(_DEBUG)
+void RTestHeap::__DbgTest(void* aPtr) const
+	{
+	RHeapDump& d = *(RHeapDump*)aPtr;
+	d.iMinLength=iMinLength;
+	d.iChunk.SetHandle(iChunkHandle);
+	d.iBase=iBase;
+	d.iTop=iTop;
+	d.iFree=iFree;
+	}
+#endif
+
+
+#if defined(_DEBUG)
+TBool Invariant(RHeap* aHeap)
+	{
+	RHeapDump dump;
+	((RTestHeap*)aHeap)->__DbgTest(&dump);
+	if(dump.iMinLength!=OrigDump.iMinLength) 		return(EFalse);
+	// Note: iChunk is a class
+	if(dump.iBase!=OrigDump.iBase)			return(EFalse);
+	if(*dump.iBase!=*OrigDump.iBase)		return(EFalse);
+	if(dump.iTop!=OrigDump.iTop)			return(EFalse);
+	if(dump.iTop[-1]!=OrigDump.iTop[-1])	return(EFalse);	
+	if(dump.iFree.len!=OrigDump.iFree.len)	return(EFalse);
+	// iFree.Next changes during allocation/freeing etc.
+	return(ETrue);
+	}
+#define INV(x) x;
+#else
+#define INV(x)
+#endif
+
+LOCAL_D RTest test(_L("T_HEAP"));
+LOCAL_D TInt heapCount=1;
+LOCAL_D RHeap *gHeapPtr;
+LOCAL_D RHeap *gHeapPtr2;
+
+class TestRHeap
+	{
+public:
+	void Test1(void);
+	void Test2(void);
+	void Test3(void);
+	void Test4(void);
+	void Test5(void);
+	void Test7(void);
+	void Test8(void);
+	void TestCompressAll(void);
+	void TestOffset(void);
+private:
+	TInt RHeapCalcReduce(TInt aCellSize, TInt aGrowBy);
+	};
+
+LOCAL_C RHeap* allocHeap(TInt aSize)
+//
+// Allocate a chunk heap with max size aSize
+//
+	{
+
+	TName n;
+	n.Format(_L("TESTHEAP%d"),heapCount++);
+	return(User::ChunkHeap(&n,aSize,aSize));
+	}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Test that methods are in the DLL
+////////////////////////////////////////////////////////////////////////////////////////
+void TestRHeap::Test1(void)
+	{ 
+	TAny* aCell;
+	TInt aVar;
+	RHeap* pHeap=allocHeap(3000); // tests first constructor indirectly 
+	// constructor with Chunk not tested
+	pHeap->Base();
+	pHeap->Size();
+	pHeap->Available(aVar);
+	pHeap->Check();
+	pHeap->Count();												 	
+	pHeap->Count(aVar);
+	aCell=pHeap->Alloc(50);
+	pHeap->Free(aCell);
+	aCell=pHeap->AllocL(50);
+	pHeap->AllocLen(aCell);
+	pHeap->ReAlloc(aCell, 100);
+	pHeap->ReAllocL(aCell, 150);
+	pHeap->Reset();
+	pHeap->Close();
+	}
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Assorted Methods 1
+//////////////////////////////////////////////////////////////////////////////
+void TestRHeap::Test2(void)
+	{
+#if defined(_DEBUG)
+	RHeapDump dump;
+	RHeap* pHeap=allocHeap(3000);
+
+	((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
+	((RTestHeap*)pHeap)->__DbgTest(&dump);
+	test(dump.iBase==pHeap->Base());
+	test((dump.iTop-dump.iBase)==pHeap->Size());
+	pHeap->Check();
+	test(Invariant(pHeap));
+	pHeap->Close();
+#endif
+	}
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Assorted Methods 2
+//////////////////////////////////////////////////////////////////////////////		 
+void TestRHeap::Test3(void)
+	{  
+	TInt CellLen;
+	TInt OrigBiggestBlock, BiggestBlock;
+	TAny* aCell;
+	TInt FreeCount, AllocCount, AllocSize;
+	RHeap* pHeap=allocHeap(5000);
+
+#if defined(_DEBUG)
+	((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
+#endif
+
+	// test AllocSize
+	AllocCount=pHeap->Count(FreeCount);
+	test(pHeap->AllocSize(AllocSize)==pHeap->Count());
+	test(AllocSize==0);
+	test(AllocCount==pHeap->Count());
+	test(AllocCount==0);
+	test(FreeCount==1);
+
+	TAny* p1=pHeap->Alloc(1);
+	test(pHeap->AllocSize(AllocSize)==1);
+	test(AllocSize==pHeap->AllocLen(p1));
+	 
+	TAny* p2=pHeap->Alloc(8);
+	test(pHeap->AllocSize(AllocSize)==2);
+	test(AllocSize==pHeap->AllocLen(p1)+pHeap->AllocLen(p2));
+
+	TAny* p3=pHeap->Alloc(127);
+	test(pHeap->AllocSize(AllocSize)==3);
+	test(AllocSize==pHeap->AllocLen(p1)+pHeap->AllocLen(p2)+pHeap->AllocLen(p3));
+
+	pHeap->Free(p2);
+	test(pHeap->AllocSize(AllocSize)==2);
+	test(AllocSize==pHeap->AllocLen(p1)+pHeap->AllocLen(p3));
+
+	pHeap->Free(p1);
+	test(pHeap->AllocSize(AllocSize)==1);
+	test(AllocSize==pHeap->AllocLen(p3));
+
+	pHeap->Free(p3);
+	test(pHeap->AllocSize(AllocSize)==0);
+	test(AllocSize==0);
+
+	pHeap->Available(OrigBiggestBlock);
+
+	// Request too large a block 
+	test((aCell=pHeap->Alloc(OrigBiggestBlock+1))==NULL);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==0);
+	test(FreeCount==1);
+
+
+	// Request block same size as that available
+	test((aCell=pHeap->Alloc(OrigBiggestBlock))!=NULL);
+	test(pHeap->Available(BiggestBlock)==0);  
+	test(BiggestBlock==0);
+	test(pHeap->AllocLen(aCell)==OrigBiggestBlock);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==pHeap->Count());
+	test(AllocCount==1);
+	test(FreeCount==0);
+	pHeap->Check();
+	// Free the block
+	pHeap->FreeZ(aCell);
+	test(aCell==NULL);
+	pHeap->Available(BiggestBlock);
+	test(BiggestBlock==OrigBiggestBlock);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==0);
+	test(FreeCount==1);
+
+
+	// Request a block much smaller than that available
+	test((aCell=pHeap->Alloc(1))!=NULL);
+	CellLen=pHeap->AllocLen(aCell);
+	pHeap->Available(BiggestBlock);
+	test(pHeap->Available(BiggestBlock)==BiggestBlock);
+	test((BiggestBlock+CellLen+KHeadSize)==OrigBiggestBlock);
+	// NOTE: if a block of 1000 was initially available, getting a cell of length 100 DOES NOT
+	// leave 900 available as some of the 1000(KHeadSize) is used up storing the length of the
+	// allocated block
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==1);
+	test(FreeCount==1);
+	pHeap->Check();
+	// Free the block
+	pHeap->Free(aCell);
+	test(aCell!=NULL);
+	pHeap->Available(BiggestBlock);
+	test(BiggestBlock==OrigBiggestBlock);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==0);
+	test(FreeCount==1);
+
+
+	// Request a block only just smaller than that available
+	test((aCell=pHeap->Alloc(OrigBiggestBlock-1))!=NULL);
+	CellLen=pHeap->AllocLen(aCell);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==1);
+	test(FreeCount==0);
+	pHeap->Check();
+	// Free the block
+	pHeap->Free(aCell);
+	pHeap->Available(BiggestBlock);
+	test(BiggestBlock==OrigBiggestBlock);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==0);
+	test(FreeCount==1);
+
+
+	//Request a block of 0 size	   Note: 0 may not necessarily be allocated (probably will be 4)
+	test((aCell=pHeap->Alloc(0))!=NULL);
+	pHeap->Available(BiggestBlock);
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==1);
+	test(FreeCount==1);
+	pHeap->Check();
+	//Free the block
+	pHeap->Free(aCell);
+	pHeap->Available(BiggestBlock);
+	test(BiggestBlock==OrigBiggestBlock);	 
+	AllocCount=pHeap->Count(FreeCount);
+	test(AllocCount==0);
+	test(FreeCount==1);
+	pHeap->Check();
+	INV(test(Invariant(pHeap)));
+
+	// close heap so we don't exceed chunk limit
+	pHeap->Close();
+	}
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Assorted Methods 3 - Here we go loopy loo, here we go loopy li
+//////////////////////////////////////////////////////////////////////////////				  
+void TestRHeap::Test4(void)
+	{ 
+	TInt OrigBiggestBlock, BiggestBlock, FreeCount, AllocCount;
+	RHeap* pHeap=allocHeap(5000);
+
+	pHeap->Available(OrigBiggestBlock);
+#if defined(_DEBUG)
+	((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
+#endif
+	
+	for(TInt ArraySize=1; ArraySize<=100; ArraySize++)
+		{	  
+		TAny** ArrayOfCells;
+		ArrayOfCells= new TAny*[ArraySize];
+		TInt ArrayIndex;
+	
+		// Allocate some cells
+		for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
+			ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3)); 
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock!=OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test((TInt)AllocCount==ArraySize);
+		test(FreeCount==1);
+		pHeap->Check();
+		// Now free them with Reset
+		pHeap->Reset();
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock==OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test(AllocCount==0);
+		test(FreeCount==1);
+	
+	
+		// Allocate some cells again	 
+		for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
+			ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3)); 
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock!=OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test((TInt)AllocCount==ArraySize);
+		test(FreeCount==1);
+		pHeap->Check();
+		// Free them with Free
+		for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
+			pHeap->Free(ArrayOfCells[ArrayIndex]);
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock==OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test(AllocCount==0);
+		test(FreeCount==1);
+	
+	
+		// Allocate some cells again	 
+		for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
+			ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock!=OrigBiggestBlock); 
+		AllocCount=pHeap->Count(FreeCount);
+		test((TInt)AllocCount==ArraySize);
+		test(FreeCount==1);
+		pHeap->Check();
+		// Free them backwards
+		for(ArrayIndex=ArraySize-1; ArrayIndex>=0; ArrayIndex--)
+			pHeap->Free(ArrayOfCells[ArrayIndex]);
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock==OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test(AllocCount==0);
+		test(FreeCount==1);
+
+
+		// Allocate some cells again	 
+		for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
+			ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock!=OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test((TInt)AllocCount==ArraySize);
+		test(FreeCount==1);
+		pHeap->Check();
+		// Free the odd cells then the even cells
+		for(ArrayIndex=0; ArrayIndex<ArraySize; ArrayIndex+=2)
+			pHeap->Free(ArrayOfCells[ArrayIndex]);
+		pHeap->Check();
+		for(ArrayIndex=1; ArrayIndex<ArraySize; ArrayIndex+=2)
+			pHeap->Free(ArrayOfCells[ArrayIndex]);
+		pHeap->Check();
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock==OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test(AllocCount==0);
+		test(FreeCount==1);
+	
+	
+		// Allocate some cells again	 
+		for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
+			ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
+		pHeap->Available(BiggestBlock);
+		test(BiggestBlock!=OrigBiggestBlock);
+		AllocCount=pHeap->Count(FreeCount);
+		test((TInt)AllocCount==ArraySize);
+		test(FreeCount==1);
+		pHeap->Check();
+		// Free one half then the other
+		for(ArrayIndex=ArraySize-1; ArrayIndex>=ArraySize/2; ArrayIndex--)
+			pHeap->Free(ArrayOfCells[ArrayIndex]);
+		for(ArrayIndex=0; ArrayIndex<ArraySize/2; ArrayIndex++)
+			pHeap->Free(ArrayOfCells[ArrayIndex]);
+		AllocCount=pHeap->Count(FreeCount);
+		test(AllocCount==0);
+		test(FreeCount==1);
+	
+		delete [] ArrayOfCells;
+		pHeap->Check();
+		INV(test(Invariant(pHeap)))
+		}
+
+	// close heap so we don't exceed chunk limit
+	pHeap->Close();
+	}
+
+	
+///////////////////////////////////////////////////////////////////////////////
+// Test ReAlloc
+//////////////////////////////////////////////////////////////////////////////
+void TestRHeap::Test5(void)
+	{
+	TInt BiggestBlock, CellSize;
+
+	RHeap* pHeap=allocHeap(5000);
+#if defined(_DEBUG)
+	((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
+#endif
+	pHeap->Available(BiggestBlock);
+	TAny* aCell=pHeap->Alloc(BiggestBlock);
+
+	// Attempt to resize the block above the space available
+	test(pHeap->ReAlloc(aCell, BiggestBlock*2)==NULL);
+
+	// Resize the block to 0
+	aCell=pHeap->ReAlloc(aCell, 0);
+	CellSize=pHeap->AllocLen(aCell);  // test?
+
+	// Resize positively
+	for(TInt aSize=0; aSize<=BiggestBlock; aSize++, pHeap->Available(BiggestBlock))
+		{
+		test(pHeap->ReAlloc(aCell, aSize)!=NULL); 
+		CellSize=pHeap->AllocLen(aCell);
+		test(CellSize>=aSize);
+		if (aSize<KMinCellLength)
+			test(CellSize==KMinCellLength);
+		else
+			test(CellSize<aSize+KAlign);
+		}
+
+	// Note: when increasing a cell size the size is rounded up to the nearest 4 but when 
+	// decreasing a cell the size is rounded down to the nearest 8 - this is due to the fact
+	// that when memory is released its size must be big enough to hold a free cell header which
+	// is greater(8) than an allocated header(4)
+	// i.e. size = 16, resize to 17 => result  is 20. But resize to 15 stays as 16, resize to 9
+	// stays as 16 but resize as 8 will resize to 8
+
+	for(TInt aSize2=(TInt)pHeap->AllocLen(aCell); aSize2>=0; aSize2--)
+		{
+		test(pHeap->ReAlloc(aCell, aSize2)!=NULL);
+		test(((TInt)pHeap->AllocLen(aCell)>=aSize2)&&((TInt)pHeap->AllocLen(aCell)<=aSize2+KMinFreeSize));
+		}
+  
+	pHeap->Check();
+	pHeap->Reset();
+	// Allocate a block, fill with data, allocate another block or two then resize the original
+	// block such that it has to be moved in memory, then check the blocks' contents 
+	TAny* Cell1=pHeap->Alloc(16);
+	TText8* pC;
+	TInt Cell1Size=pHeap->AllocLen(Cell1);
+	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; *pC++='x')
+		;
+	TAny* Cell2=pHeap->Alloc(16);
+	TInt Cell2Size=pHeap->AllocLen(Cell2);
+	for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+pHeap->AllocLen(Cell2); *pC++='y')
+		;
+	Cell1=pHeap->ReAlloc(Cell1, 128);
+	// Test data was copied on reallocation
+	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
+		; 
+	// Test other data wasn't corrupted
+	for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+pHeap->AllocLen(Cell2); test(*pC++=='y'))
+		;
+
+	// Allocate another block
+	TAny* Cell3=pHeap->Alloc(8);
+	for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); *pC++='z')
+		;
+	// test existing blocks to be safe
+	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
+		;
+	for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
+		;
+	// Resize previous blocks
+	Cell1=pHeap->ReAlloc(Cell1, 16); // Shrink previously expanded block
+	Cell2=pHeap->ReAlloc(Cell2, 64); 
+	// Now test data
+	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
+		;
+	for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
+		;
+	for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'))
+		;
+
+	// Re-expand Cell1
+	Cell1=pHeap->ReAlloc(Cell1, 1028);
+	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
+		;
+	for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
+		;
+	for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'))
+		;
+
+	// Shrink cells back to original size
+	Cell1=pHeap->ReAlloc(Cell1, Cell1Size);
+	Cell2=pHeap->ReAlloc(Cell2, Cell2Size);  
+	for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
+		;
+	for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
+		;
+	for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'))
+		;
+
+	pHeap->Check();
+	INV(test(Invariant(pHeap)));
+
+	// close heap so we don't exceed chunk limit
+	pHeap->Close();
+	}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Test walking methods (more thoroughly than previously)
+//////////////////////////////////////////////////////////////////////////////				  
+void TestRHeap::Test7(void)
+	{ 
+	TInt NumAllocated=0, NumFree=1, i;
+	RHeap* pHeap=allocHeap(5000);
+
+	TAny** ArrayOfCells;
+	ArrayOfCells= new TAny*[100];
+
+	for(i=0; i<100; i++)
+		{
+		ArrayOfCells[i]=pHeap->Alloc(8);
+		NumAllocated++;
+		test(NumAllocated==pHeap->Count(NumFree));
+		test(NumFree==1);
+		}
+	pHeap->Check();
+
+	for(i=0; i<100; i+=2)
+		{
+		TInt temp;
+		pHeap->Free(ArrayOfCells[i]);
+		NumAllocated--;
+		NumFree++;
+		test(NumAllocated==pHeap->Count(temp));
+		test(NumFree==temp);
+		}
+	pHeap->Check();
+	pHeap->Reset();
+
+
+	///////////////////////////////////////////
+	// Corrupt data and see what happens
+	///////////////////////////////////////////
+	// Corrupt  allocated cell header
+	ArrayOfCells[0]=pHeap->Alloc(32);
+	TUint32* pC=(TUint32*)ArrayOfCells[0]-KHeadSize; 
+	*pC=0xa5a5a5a5u; 
+	// pHeap->Check();
+
+	// Corrupt free cell header 
+	pHeap->Reset();
+	ArrayOfCells[0]=pHeap->Alloc(32);
+	pC=(TUint32*)ArrayOfCells[0]+(pHeap->AllocLen(ArrayOfCells[0])>>2);
+	*pC=0xa1a1a1a1u;	
+	//pHeap->Check();	 // Check doesn't pick it up but an access violation is generated
+
+	// Write past end of heap
+	pHeap->Reset();
+	TInt Avail;
+	ArrayOfCells[0]=pHeap->Alloc(pHeap->Available(Avail));
+	pC=(TUint32*)ArrayOfCells[0]+(pHeap->AllocLen(ArrayOfCells[0])>>2);
+	//*pC=0xa1a1a1a1u;	// This line isn't picked up by Check (wouldn't expect it to) but the call
+	//pHeap->Check();	// to delete below consequently crashes 
+
+	delete [] ArrayOfCells;
+
+	// close heap so we don't exceed chunk limit
+	pHeap->Close();
+	}
+
+//////////////////////////////////////
+// Test the leave methods
+//////////////////////////////////////
+void TestRHeap::Test8(void)
+	{ 
+
+	TAny* aCell=NULL;
+	RHeap* pHeap=allocHeap(1000); 
+	TRAPD(ret,aCell=pHeap->AllocL(100))
+	test(ret==KErrNone);
+	TRAP(ret,aCell=pHeap->AllocL(PageSize))
+   	test(ret==KErrNoMemory);
+	TRAP(ret,aCell=pHeap->ReAllocL(aCell,32))
+	test(ret==KErrNone);
+	TRAP(ret,aCell=pHeap->ReAllocL(NULL,10000))
+	test(ret==KErrNoMemory);
+
+	// close heap so we don't exceed chunk limit
+	pHeap->Close();
+	}
+
+class RMyHeap : public RHeap
+	{
+public:
+	void MyCompressAll(){}
+private:
+	RMyHeap();
+	};
+
+#include "TestRHeapShrink.h"
+
+/**
+	Calculates whether or not the heap with iGrowBy=aGrowBy will be reduced if a 
+	cell of size aCellSize bytes is the top free cell.
+	It must be calculated as both the page size and min cell size could vary
+	between different platforms/builds.  Also, KHeapMinCellSize is 'patchdata' and can be
+	different for particular ROM builds
+	ASSUMPTIONS:-
+		1 - The cell of aCellSize starts past the RHeap's iMinLength (i.e. all of it can be 
+		removed without the RHeap becoming smaller than iMinLength
+		2 - The default value of aAlign was passed to RHeap contructor
+	These should be safe as this is onl used by t_heap TestRHeap::CompressAll()
+	@return The number of bytes the heap will be reduced by
+*/
+TInt TestRHeap::RHeapCalcReduce(TInt aCellSize, TInt aGrowBy)
+	{
+	TInt ret = 0;
+	TInt pageSize = 0;
+	test(UserHal::PageSizeInBytes(pageSize)==KErrNone);
+	
+	// adjust aGrowBy to match what RHeap would have aligned its iGrowBy to 
+	// see RHeap::RHeap()
+	aGrowBy = _ALIGN_UP(aGrowBy, pageSize);
+	if (aCellSize >= KHeapShrinkHysRatio*(aGrowBy>>8))
+		{
+		//calc for amount to reduce heap from RHeap::Reduce()
+		// assumes that cell of aCellSize starts past the RHeap's iMinLength
+		ret=_ALIGN_DOWN(aCellSize, pageSize);
+		}
+	return ret;
+	}
+
+void TestRHeap::TestCompressAll()
+	{
+
+	TPtrC myHeapName=_L("MyHeap");
+	// myHeap will have default GrowBy of KMinHeapGrowBy
+	RMyHeap* myHeap=(RMyHeap*)User::ChunkHeap(&myHeapName,0x100,0x2000);
+const TInt KnormHeapGrowBy = 0x2000;
+	RHeap* normHeap=User::ChunkHeap(NULL,0x100,0x20000,KnormHeapGrowBy);
+
+	TAny* ptrMy1=myHeap->Alloc(0x102);
+	test(ptrMy1!=NULL);
+	TAny* ptrMy2=myHeap->Alloc(0x1001);
+	test(ptrMy2!=NULL);
+	TInt r=myHeap->Count();
+	test(r==2);
+
+	TAny* ptrNorm1=normHeap->Alloc(0x8002);
+	test(ptrNorm1!=NULL);
+	TAny* ptrNorm2=normHeap->Alloc(0x12fff);
+	test(ptrNorm2!=NULL);
+	TAny* ptrNorm3=normHeap->Alloc(0x334f);
+	test(ptrNorm3!=NULL);
+	r=normHeap->Count();
+	test(r==3);
+
+	TInt oldMyHeapSize=myHeap->Size(); 	
+	TInt oldNormHeapSize=normHeap->Size();
+	 	
+	myHeap->MyCompressAll();
+
+	r=myHeap->Count();
+	test(r==2);
+	r=myHeap->Size();
+	test(r==oldMyHeapSize);
+	r=normHeap->Count();
+	test(r==3);
+	r=normHeap->Size();
+	test(r==oldNormHeapSize);
+
+	// Remove the cell on the top of the normHeap
+	normHeap->Free(ptrNorm3);
+	// check myHeap unaffected
+	r=myHeap->Count();
+	test(r==2);
+	r=myHeap->Size();
+	test(r==oldMyHeapSize);
+	//check normHeap updated after free of top cell
+	r=normHeap->Count();
+	test(r==2);
+	r=normHeap->Size();
+
+	// Calc the amount, if any, the overall size of normHeap will have been shrunk by
+	// will depend on value of KHeapShrinkHysRatio.
+	// 1st calc current total size of the allocated cells
+	TInt normAllocdSize = 	normHeap->AllocLen(ptrNorm1)+RHeap::EAllocCellSize +
+							normHeap->AllocLen(ptrNorm2)+RHeap::EAllocCellSize;
+	TInt normReduce = RHeapCalcReduce(oldNormHeapSize-normAllocdSize,KnormHeapGrowBy);
+	oldNormHeapSize -= normReduce;
+	test(r==oldNormHeapSize);
+
+	normHeap->Free(ptrNorm2);
+	myHeap->Free(ptrMy2);
+	r=myHeap->Count();
+	test(r==1);
+	r=myHeap->Size();
+
+	// Calc the current total size of the allocated cells
+	TInt myAllocdSize = myHeap->AllocLen(ptrMy1)+RHeap::EAllocCellSize;
+	TInt myReduce=RHeapCalcReduce(oldMyHeapSize-myAllocdSize,1);
+	oldMyHeapSize -= myReduce;
+	test(r==oldMyHeapSize);
+
+	r=normHeap->Count();
+	test(r==1);
+	r=normHeap->Size();
+
+	// cell represented by ptrNorm3 may have already caused the heap
+	// size to be reduced so ensure normReduce is factored into calcs
+	test(r==oldNormHeapSize-(0x16000-normReduce));
+
+	myHeap->Close();
+	normHeap->Close();
+	}
+
+
+void TestRHeap::TestOffset()
+	{
+	TInt size = 0x100000;
+	const TInt offset = 0x8;
+	const TUint8 magic = 0x74; // arbitrary magic value
+	RChunk chunk;
+	RHeap* heap;
+	
+	chunk.CreateLocal(0, size);
+	size = chunk.MaxSize();	// X86 has 4MB chunk size
+	
+	// try and create a heap with a large offset - no room to make RHeap, should fail
+	heap = UserHeap::OffsetChunkHeap(chunk, 0, size);
+	test(heap==NULL);
+	
+	// write some magic numbers into the offset-reserved area
+	chunk.Adjust(offset);
+	TUint8* reserved = chunk.Base();
+	TUint8* limit = reserved + offset;
+	for (; reserved<limit; reserved++)
+		*reserved = magic;
+	
+	// make a heap with an offset
+	heap = UserHeap::OffsetChunkHeap(chunk, 0, offset);
+	test(heap!=NULL);
+	test(chunk.Base() + offset == (TUint8*)heap);
+	TInt origsize = heap->Size();
+
+	// force the heap to grow to the maximum size by allocating 1kb blocks
+	// and then allocating whatever is left. Check this really is the end
+	// of the chunk.
+	TUint8* temp = NULL;
+	TUint8* last = NULL;
+	do
+		{
+		last = temp;
+		temp = (TUint8*)heap->Alloc(1024);
+		}
+	while (temp != NULL);
+	TInt biggestblock, space;
+	space = heap->Available(biggestblock);
+	if (space>0)
+		{
+		last = (TUint8*)heap->Alloc(space);
+		test(last!=NULL);
+		// Check that the last allocation doesn't pass the end of the chunk
+		test(last+space <= chunk.Base()+size);
+		// but that it is within the alignment requirement, as less than this
+		// would be short of the end
+		test(last+space > chunk.Base()+size-RHeap::ECellAlignment);
+		}
+	else
+		{
+		test(last+1024 == chunk.Base()+size);
+		}
+	
+	// try writing at the top end of it to make sure it's backed
+	*(chunk.Base()+size-1) = 1;
+	
+	// test resetting the heap
+	heap->Reset();
+	test(origsize == heap->Size());
+	
+	// check reducing the heap works
+	last = (TUint8*)heap->Alloc(size>>2);
+	TInt midsize = heap->Size();
+	temp = (TUint8*)heap->Alloc(size>>2);
+	heap->Free(temp);
+	heap->Compress();
+	test(midsize == heap->Size());
+	heap->Free(last);
+	heap->Compress();
+	test(origsize == heap->Size());
+	
+	// check the magic numbers are still there
+	for (reserved = chunk.Base(); reserved<limit; reserved++)
+		test(*reserved==magic);
+	
+	heap->Close();
+	}
+
+
+RSemaphore sem;
+LOCAL_C void syncThreads(TAny* anArg)
+//
+// get the threads both running at the same time
+//
+	{
+	if ((TInt)anArg==1)
+		sem.Wait();
+	else
+		sem.Signal();
+	}
+
+TInt comeInNumber=0;
+LOCAL_C TInt sharedHeapTest1(TAny* anArg)
+//
+// Shared heap test thread.
+//
+	{
+
+	RHeap* pH = (RHeap*)&User::Allocator();
+	if (gHeapPtr && pH!=gHeapPtr)
+		return(KErrGeneral);
+	gHeapPtr2 = pH;
+
+	syncThreads(anArg);
+
+	TAny* a[0x100];
+	TInt mod=((TInt)anArg)*3;
+
+	// Run in a timed loop, to ensure that we get some true concurrency
+	RTimer timer;
+	TTime now;
+	TRequestStatus done;
+	test(timer.CreateLocal()==KErrNone);
+	now.HomeTime();
+	timer.At(done,now+TTimeIntervalSeconds(20));
+
+	while (done==KRequestPending && comeInNumber!=(TInt)anArg)
+		{
+		TInt i=0;
+		for (;i<0x100;i++)
+			{
+			a[i]=User::Alloc(0x10);
+			test(a[i]!=NULL);
+			Mem::Fill(a[i],0x10,(((TInt)anArg)<<4)|(i&0x0F));	// marker
+			if ((i%mod)==0)
+				pH->Check();
+			}
+		for (i=0;i<0x100;i++)
+			{
+			User::Free(a[i]);
+			if ((i%mod)==0)
+				pH->Check();
+			}
+		}
+	timer.Cancel();
+	return((TInt)anArg);
+	}
+
+LOCAL_C void bumpKernelGranularity()
+//
+// Push up the kernels granularities
+//
+	{
+
+	RThread t[4];
+	TInt r;
+	TUint i=0;
+	for (;i<4;i++)
+		{
+		TName n;
+		n.Format(_L("Temp%d"),i);
+		r=t[i].Create(n,sharedHeapTest1,KDefaultStackSize,NULL,NULL);
+		test(r==KErrNone);
+		}
+	for (i=0;i<4;i++)
+		{
+		t[i].Kill(KErrNone);
+		t[i].Close();
+		}
+	}
+
+LOCAL_C void createTestThreads(TThreadFunction aFunction,RHeap* aHeap)
+//
+// Create two test threads using the supplied entry point and heap
+//
+	{
+
+
+	test.Next(_L("Create t1"));
+	RThread t1;
+	TInt r=t1.Create(_L("Shared1"),aFunction,KDefaultStackSize,aHeap,(TAny*)1);
+	test(r==KErrNone);
+	TRequestStatus tStat1;
+	t1.Logon(tStat1);
+	test(tStat1==KRequestPending);
+
+	test.Next(_L("Create t2"));
+	RThread t2;
+	r=t2.Create(_L("Shared2"),aFunction,KDefaultStackSize,aHeap,(TAny*)2);
+	test(r==KErrNone);
+	TRequestStatus tStat2;
+	t2.Logon(tStat2);
+	test(tStat2==KRequestPending);
+
+	test.Next(_L("Wait for t1 or t2 - approx 20 seconds"));
+	t1.Resume();
+	t2.Resume();
+	User::WaitForRequest(tStat1,tStat2);
+	User::WaitForRequest(tStat1==KRequestPending ? tStat1 : tStat2);
+	test(tStat1==1);
+	test(tStat2==2);
+	CLOSE_AND_WAIT(t1);
+	CLOSE_AND_WAIT(t2);
+	}
+
+LOCAL_C void SharedHeapTest1()
+//
+// Shared heap test using normal chunk heap
+//
+	{
+
+	sem.CreateLocal(0);	// create synchronisation semaphore
+	test.Start(_L("Create chunk to share"));
+	TPtrC sharedHeap=_L("SharedHeap");
+	TInt minsize = ((RHeap&)User::Allocator()).Size();
+	gHeapPtr=User::ChunkHeap(&sharedHeap,minsize/*0x20000*/,0x40000);
+	test(gHeapPtr!=NULL);
+	TInt count=gHeapPtr->Count();
+	createTestThreads(sharedHeapTest1,gHeapPtr);
+	test(count==gHeapPtr->Count());
+	gHeapPtr->Close();
+	test.End();
+	}
+
+LOCAL_C void SharedHeapTest2()
+//
+// Shared heap test using the current threads heap. Can test kernel
+// cleanup since granularity will have been handled by running
+// SharedHeapTest2().
+//
+	{
+
+	test.Start(_L("Current chunk to share"));
+	test.Next(_L("Bump up granularities"));
+//
+// First create a number of threads to push up the kernels granularities
+//
+	bumpKernelGranularity();
+//
+	__KHEAP_MARK;
+	gHeapPtr = (RHeap*)&User::Allocator();
+	TInt biggest1;
+	TInt avail1=gHeapPtr->Available(biggest1);
+	TInt size1=gHeapPtr->Size();
+
+	createTestThreads(sharedHeapTest1,NULL);
+	
+	TInt biggest2;
+	TInt avail2=gHeapPtr->Available(biggest2);
+	TInt size2=gHeapPtr->Size();
+	test.Printf(_L("Before: size %d, %d available (biggest %d)\r\n"),size1,avail1,biggest1);
+	test.Printf(_L("After:  size %d, %d available (biggest %d)\r\n"),size2,avail2,biggest2);
+	test((size1-avail1)==(size2-avail2));	// no leaks
+	if (avail1==biggest1)	// if it was a single block of free space before
+		test(avail2==biggest2);	// then it should still be a single block
+	__KHEAP_MARKEND;
+	test.End();
+	}
+
+LOCAL_C void SharedHeapTest3()
+//
+// Shared heap test borrowing a thread's default heap and
+// killing threads in different orders.
+//
+	{
+
+	test.Start(_L("Create t1 whose heap will be shared"));
+	gHeapPtr = NULL;
+	RThread t1;
+	TInt r=t1.Create(_L("Owner_T1"),sharedHeapTest1,KDefaultStackSize,0x20000,0x40000,(TAny*)1);
+	test(r==KErrNone);
+	TRequestStatus tStat1;
+	t1.Logon(tStat1);
+	test(tStat1==KRequestPending);
+	t1.SetPriority(EPriorityMore); //t1 gets to wait on semaphore sem, before we start t2
+	t1.Resume();
+	test.Next(_L("Create t2 sharing t1's heap"));
+	RThread t2;
+	r=t2.Create(_L("Sharer_T2"),sharedHeapTest1,KDefaultStackSize,gHeapPtr2,(TAny*)2);
+	test(r==KErrNone);
+	TRequestStatus tStat2;
+	t2.Logon(tStat2);
+	test(tStat2==KRequestPending);
+
+	test.Next(_L("Get t1 to exit while t2 continues running"));
+	test(tStat1==KRequestPending);
+	test(tStat2==KRequestPending);
+	t1.SetPriority(EPriorityNormal); //back to the same priority as t2
+	t2.Resume();
+	test(tStat1==KRequestPending);
+	test(tStat2==KRequestPending);
+	comeInNumber=1;
+	test.Next(_L("Wait for t1"));
+	User::WaitForRequest(tStat1);
+	test(tStat1==1);
+	test(t1.ExitType()==EExitKill);
+	test(t1.ExitReason()==1);
+	test(tStat2==KRequestPending);
+	test(t2.ExitType()==EExitPending);
+	test.Next(_L("Wait for t2"));
+	User::WaitForRequest(tStat2);
+	test(tStat2==2);
+	test(t2.ExitType()==EExitKill);
+	test(t2.ExitReason()==2);
+	CLOSE_AND_WAIT(t2);
+	CLOSE_AND_WAIT(t1);
+	test.End();
+	}
+
+LOCAL_C void TestAuto()
+//
+// Test heap auto expansion and compression
+//
+	{
+
+	test.Start(_L("Create chunk to"));
+	TPtrC autoHeap=_L("AutoHeap");
+	gHeapPtr=User::ChunkHeap(&autoHeap,0x1800,0x6000);
+	test(gHeapPtr!=NULL);
+	TInt biggest;
+	TInt avail=gHeapPtr->Available(biggest);
+	test(avail==biggest);
+	TAny *p1=gHeapPtr->Alloc(biggest);
+	test(p1!=NULL);
+	TAny *p2=gHeapPtr->Alloc(biggest);
+	test(p2!=NULL);
+	TAny *p3=gHeapPtr->Alloc(biggest);
+	test(p3!=NULL);
+	TAny *p4=gHeapPtr->Alloc(biggest);
+	test(p4==NULL);
+	TInt comp=gHeapPtr->Compress();
+	test(comp==0);
+	gHeapPtr->Free(p2);
+	comp=gHeapPtr->Compress();
+	test(comp==0);
+	gHeapPtr->Free(p3);
+	comp=gHeapPtr->Compress();
+// stop wins compiler warning of constant expression as KHeapShrinkHysRatio
+// isn't constant for non-emulator builds but ROM 'patchdata'
+#pragma warning(disable : 4127)
+	// When hysteresis value > 4.0*GrowBy then Free() calls
+	// won't shrink heap but normally will shrink heap
+	if (KHeapShrinkHysRatio <= 1024)
+		test(comp==0);
+	else
+		test(comp==0x4000);
+#pragma warning(default : 4127)
+	gHeapPtr->Free(p1);
+	comp=gHeapPtr->Compress();
+	test(comp==0);
+	TInt biggest1;
+	TInt avail1=gHeapPtr->Available(biggest1);
+	test(avail1==avail1);
+	test(biggest==biggest1);
+	test(gHeapPtr->Count()==0);
+	gHeapPtr->Close();
+	test.End();
+	}
+
+
+GLDEF_C TInt E32Main(void)
+	{
+
+	test.Title();
+
+	__KHEAP_MARK;
+
+	test.Start(_L("Test 1"));
+	UserHal::PageSizeInBytes(PageSize);
+        TestRHeap T;
+	T.Test1();
+	test.Next(_L("Test auto expand and compress"));
+	TestAuto();
+	test.Next(_L("Test 2"));
+	T.Test2();
+	test.Next(_L("Test 3"));
+	T.Test3();
+	test.Next(_L("Test 4"));
+	T.Test4();
+	test.Next(_L("Test 5"));
+	T.Test5();
+	test.Next(_L("Test 7"));
+	T.Test7();
+	test.Next(_L("Test 8"));
+	T.Test8();
+	test.Next(_L("Test CompressAll()"));
+	T.TestCompressAll();
+	test.Next(_L("Test offset heap"));
+	T.TestOffset();
+	test.Next(_L("Shared heap test 1"));
+	SharedHeapTest1();
+	test.Next(_L("Shared heap test 2"));
+	SharedHeapTest2();
+	test.Next(_L("Shared heap test 3"));
+	SharedHeapTest3();
+	sem.Close();
+
+	__KHEAP_CHECK(0);
+	__KHEAP_MARKEND;
+//
+	test.End();
+	return(0);
+    }
+