kerneltest/e32test/heap/t_heap.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 19 4a8fed1c0ef6
child 43 c1f20ce4abcf
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) 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 aTmpSize2 = Max(_ALIGN_UP(aSize2 + RHeap::EAllocCellSize, KAlign), KMinFreeSize);

		test((TInt)pHeap->AllocLen(aCell)<=aTmpSize2+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);
    }