// 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);
    }

