// 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\buffer\t_bma.cpp
// Overview:
// Test the bitmap allocation abilities of the CBitMapAllocator class.
// API Information:
// CBitMapAllocator.
// Details:
// - Create an instance of CBitMapAllocator class with positive size using New and NewL methods, 
// verify that object is created and deleted successfully, test the heap allocation failure. 
// - Verify that the heap has not been corrupted by the test.
// - Test Alloc, AllocFromTop, AllocAt, Free, and AllocFromTopFrom methods of 
// CBitMapAllocator class are as expected.
// - Allocate all available memory using Alloc, AllocFromTop, AllocFromTopFrom
// and check that available free space is zero.
// - Allocate more than available memory using Alloc, AllocFromTop,
// AllocFromTopFrom and check the return value is KErrorNoMemory.
// - Free the memory and check that available free space is equal to the size.
// - Allocate at specified blocks, check the allocation and available free block 
// is as expected.
// - Free the block and check the available space is as expected. 
// - Check the alignment of blocks after allocation is as expected.
// - Perform all of the above tests for CBitMapAllocator size of 1, 4, 32, 33, 68, 96, 64, 65 and 63 bits.
// - Allocate some contiguous pages of RAM from the kernel's free page pool with pattern of 
// increasingly large gaps and test that the pages are allocated as specified.
// - Check KErrorNoMemory is returned when extracting a page beyond the available space.
// - Perform a test specifically for defect EXT-5AMDKP, Alloc, Free and ExtractRamPages. Test for 
// expected results.
// - Test whether the heap has been corrupted by any of the tests.
// Platforms/Drives/Compatibility:
// All 
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#include <e32test.h>
#include <e32base.h>
#include <e32base_private.h>
#include <e32def.h>
#include <e32def_private.h>

const TInt KMaxAllocations=50;

LOCAL_D RTest test(_L("T_BMA"));

LOCAL_C void testNew(TInt aSize)
//
//	Test New
//
	{

	test.Start(_L("New"));
	__UHEAP_MARK;
	CBitMapAllocator* pBitMapAllocator=CBitMapAllocator::New(aSize);
	test(pBitMapAllocator!=NULL);
	test(pBitMapAllocator->Size()==pBitMapAllocator->Avail());
	delete pBitMapAllocator;
	__UHEAP_CHECK(0);
	for (TInt i=1;i<KMaxAllocations;i++)
		{
		test.Printf(_L("Try %d\n"),i);
		__UHEAP_SETFAIL(RHeap::EDeterministic,i);
		pBitMapAllocator=CBitMapAllocator::New(aSize);
		if (pBitMapAllocator!=NULL)
			break;
		__UHEAP_CHECK(0);
		}
	delete pBitMapAllocator;
	__UHEAP_MARKEND;
	__UHEAP_RESET;
	test.End();
	}

LOCAL_C void testNewL(TInt aSize)
//
//	Test NewL
//
	{

	test.Start(_L("NewL"));
	__UHEAP_MARK;
	CBitMapAllocator* pBitMapAllocator=CBitMapAllocator::NewL(aSize);
	test(pBitMapAllocator!=NULL);
	test(pBitMapAllocator->Size()==pBitMapAllocator->Avail());
	delete pBitMapAllocator;
	__UHEAP_CHECK(0);
	test.Next(_L("Repetitive NewL"));
	for (TInt i=1;i<KMaxAllocations;i++)
		{
		test.Printf(_L("Try %d\n"),i);
		__UHEAP_SETFAIL(RHeap::EDeterministic,i);
		TRAPD(r,pBitMapAllocator=CBitMapAllocator::NewL(aSize));
		if (r==KErrNone)
			break;
		__UHEAP_CHECK(0);
		}
	delete pBitMapAllocator;
	__UHEAP_MARKEND;
	__UHEAP_RESET;
  	test.End();	
	}

LOCAL_C void testAlloc(TInt aSize)
//
//	Test Alloc, AllocFromTop, AllocAt, and Free, and AllocFromTopFrom
//
	{

	CBitMapAllocator* pBitMapAllocator=CBitMapAllocator::New(aSize);
	test(pBitMapAllocator!=NULL);
	test.Start(_L("Alloc all available"));
	TInt available=pBitMapAllocator->Avail();
	TInt i=0;
	for (;i<available;i++)
		{
		TInt j=pBitMapAllocator->Alloc();
		test(j==i);
		}
	test(pBitMapAllocator->Avail()==0);
//
	test.Next(_L("Try to alloc more than available"));
	i=pBitMapAllocator->Alloc();
	test(i==KErrNoMemory);
//
	test.Next(_L("Free"));
	for (i=0;i<available;i++)
		pBitMapAllocator->Free(i);
	test(pBitMapAllocator->Avail()==pBitMapAllocator->Size());
//
	test.Next(_L("AllocFromTop"));	
	for (i=available-1;i>=0;i--)
		{
		TInt j=pBitMapAllocator->AllocFromTop();
		test(j==i);
		}
	test(pBitMapAllocator->Avail()==0);
//
	test.Next(_L("Try to AllocFromTop more than available"));
	i=pBitMapAllocator->AllocFromTop();
	test(i==KErrNoMemory);
//
	test.Next(_L("Free (again)"));
	for (i=0;i<available;i++)
		pBitMapAllocator->Free(i);
	test(pBitMapAllocator->Avail()==pBitMapAllocator->Size());
//
	test.Next(_L("AllocAt"));
	pBitMapAllocator->AllocAt(aSize-1);
	test(pBitMapAllocator->Avail()==pBitMapAllocator->Size()-1);
//
//	test.Next(_L("AllocAt an already allocated cell"));	// this test should cause a Panic.
//	pBitMapAllocator->AllocAt(aSize-1);
//	test(pBitMapAllocator->Avail()==pBitMapAllocator->Size()-1);
//
	test.Next(_L("Free (again)"));
	pBitMapAllocator->Free(aSize-1);
	test(pBitMapAllocator->Avail()==pBitMapAllocator->Size());
//
	test.Next(_L("AllocFromTopFrom"));
	TInt x;
	for (x=available-1;x>0;x--)
		{
		for (i=x;i>=0;i--)
			{
			TInt j=pBitMapAllocator->AllocFromTopFrom(x);
			test(j==i);
			test(!pBitMapAllocator->IsFree(j));
			}
		test(pBitMapAllocator->Avail()==available-x-1);

		test.Next(_L("Try to AllocFromTopFrom more than available"));
		i=pBitMapAllocator->AllocFromTopFrom(x);
		test(i==KErrNoMemory);
//
		TInt y;
		for (y=0;y<=x;y++)
			{
			for (i=0;i<=x;i++)
				{
				if (pBitMapAllocator->Avail()<=available-x-1)
					pBitMapAllocator->Free(y);
				TInt j=pBitMapAllocator->AllocFromTopFrom(i);
				if (i<y)
					test(j==KErrNoMemory);
				else
					{
					test(j==y);
					test(!pBitMapAllocator->IsFree(j));
					}
				}
			}
	
//
		test.Next(_L("Free (again)"));
		for (i=0;i<=x;i++)
			pBitMapAllocator->Free(i);
		test(pBitMapAllocator->Avail()==pBitMapAllocator->Size());
		}
//
	for (x=available-1;x>0;x--)
		{
		for (i=x;i>=0;i--)
			{
			TInt j=pBitMapAllocator->AllocFromTopFrom(x);
			test(j==i);
			}
		test(pBitMapAllocator->Avail()==available-x-1);

		test.Next(_L("Try to AllocFromTopFrom more than available"));
		i=pBitMapAllocator->AllocFromTopFrom(x);
		test(i==KErrNoMemory);
	//
		test.Next(_L("Free (again)"));
		for (i=0;i<=x;i++)
			pBitMapAllocator->Free(i);
		test(pBitMapAllocator->Avail()==pBitMapAllocator->Size());
		}
	test.End();
	delete pBitMapAllocator;
	}

LOCAL_C void testBlock(TInt aSize)
//
// Test Alloc(TInt, TInt&), AllocAligned, AllocAlignedBlock, AllocAt(TInt, TInt),
// IsFree(TInt, TInt), Free(TInt, TInt)
//
	{
	CBitMapAllocator* pB=CBitMapAllocator::New(aSize);
	test(pB!=NULL);
	test.Start(_L("AllocAt block, Free block, IsFree block"));
	TInt available=pB->Avail();
	test(available==aSize);
	TInt start, len;
	for(start=0; start<available; start++)
		{
		for(len=1; len<=available-start; len++)
			{
			pB->AllocAt(start,len);
			test(pB->Avail()==available-len);
			for(TInt i=0; i<available; i++)
				{
				if (i>=start && i<start+len)
					{
					if(pB->IsFree(i))
						test(0);
					}
				else
					{
					if(!pB->IsFree(i))
						test(0);
					}
				}
			if (start)
				test(pB->IsFree(0,start));
			test(!pB->IsFree(0,start+1));
			if (start+len<available)
				{
				test(pB->IsFree(start+len,available-(start+len)));
				test(!pB->IsFree(start+len-1,available-(start+len-1)));
				}
			pB->Free(start,len);
			test(pB->Avail()==available);
			test(pB->IsFree(start,len));
			test(pB->IsFree(0,available));
			}
		}
	test.End();
	test.Start(_L("Alloc consecutive block"));
	TInt askfor, init, pos, consec;
	for(askfor=1; askfor<=available; askfor++)
		{
		test.Printf(_L("Ask for %d\n"),askfor);
		for(init=0; init<available; init++)
			{
			if (init)
				pB->AllocAt(0,init);
			for(pos=init+1; pos<available; pos++)
				{
				pB->AllocAt(pos);
				TInt firstfree=pB->Alloc(askfor, consec);
				if (firstfree!=init)
					test(0);
				TInt number=(pos-init>askfor)?askfor:pos-init;
				if (consec!=number)
					test(0);
				if (number<pos-init)
					{
					firstfree=pB->Alloc(pos-init-number,consec);
					if(firstfree!=init+number)
						test(0);
					if(consec!=pos-init-number)
						test(0);
					}
				test(pB->Avail()==available-pos-1);
				TInt freeto=available;
				if (pos<available-1)
					{
					firstfree=pB->Alloc(askfor,consec);
					number=(available-pos-1>askfor)?askfor:available-pos-1;
					if (firstfree!=pos+1)
						test(0);
					if (consec!=number)
						test(0);
					freeto=pos+1+number;
					}
				test(pB->Avail()==available-freeto);
				if (available==freeto)
					{
					firstfree=pB->Alloc(1,consec);
					if (firstfree!=KErrNoMemory)
						test(0);
					if (consec!=0)
						test(0);
					}
				pB->Free(init,freeto-init);
				}
			if (init)
				pB->Free(0,init);
			test(pB->Avail()==available);
			}
		}
	test.End();
	test.Start(_L("AllocAligned"));
	TInt alignment, alignstep;
	for(alignment=0, alignstep=1; alignstep<available; alignment++, alignstep<<=1 )
		{
		TInt numaligned=(available+alignstep-1)/alignstep;
		TInt next=0;
		TInt r;
		do	{
			r=pB->AllocAligned(alignment);
			if (r>=0)
				{
				if (r!=next)
					test(0);
				next+=alignstep;
				}
			else if (r!=KErrNoMemory)
				test(0);
			} while(r>=0);
		if (pB->Avail()!=available-numaligned)
			test(0);
		for(TInt i=0; i<available; i++)
			{
			if (i==((i>>alignment)<<alignment) )
				{
				if (pB->IsFree(i))
					test(0);
				pB->Free(i);
				}
			else
				{
				if (!pB->IsFree(i))
					test(0);
				}
			}
		test(pB->Avail()==available);
		}
	test.End();
	test.Start(_L("AllocAlignedBlock"));
	for(alignment=0, alignstep=1; alignstep<available; alignment++, alignstep<<=1 )
		{
		TInt numalignedblocks=available/alignstep;
		TInt next=0;
		TInt r;
		do	{
			r=pB->AllocAlignedBlock(alignment);
			if (r>=0)
				{
				if (r!=next)
					test(0);
				next+=alignstep;
				}
			else if (r!=KErrNoMemory)
				test(0);
			} while(r>=0);
		if (pB->Avail()!=available-numalignedblocks*alignstep)
			test(0);
		if (pB->Avail()!=0)
			{
			if ( !pB->IsFree(numalignedblocks*alignstep,pB->Avail()) )
				test(0);
			r=pB->Alloc();
			if (r!=numalignedblocks*alignstep)
				test(0);
			pB->Free(r);
			}
		pB->Free(0,numalignedblocks*alignstep);
		if (pB->Avail()!=available)
			test(0);
		TInt freepos, blockpos, c;
		for (freepos=0; freepos<available; freepos+=alignstep)
			{
			for (blockpos=0; blockpos<alignstep; blockpos++)
				{
				c=0;
				for(TInt i=blockpos; i<freepos; i+=alignstep)
					{
					pB->AllocAt(i);
					c++;
					}
				if (pB->Avail()!=available-c)
					test(0);
				r=pB->AllocAlignedBlock(alignment);
				if (available-freepos<alignstep)
					{
					if (r!=KErrNoMemory)
						test(0);
					if (pB->Avail()!=available-c)
						test(0);
					}
				else
					{
					if (r!=freepos)
						test(0);
					if (pB->Avail()!=available-c-alignstep)
						test(0);
					pB->Free(freepos,alignstep);
					if (pB->Avail()!=available-c)
						test(0);
					}
				for(TInt j=blockpos; j<freepos; j+=alignstep)
					pB->Free(j);
				if (pB->Avail()!=available)
					test(0);
				}
			}
		}
	delete pB;
	test.End();
	}

LOCAL_C void testContiguousAllocation(TInt aSize)
	{//test RemoveRamPages()
	//set up bitmap with pattern of increasingly large gaps -
	//page 1      - in use,page  2        - free
	//pages 3,4   - in use,pages 5,6      - free
	//pages 7,8,9 - in use,pages 10,11,12 - free  ...etc
	test.Start(_L("Create swiss cheese effect..."));

 	CBitMapAllocator* pB=CBitMapAllocator::New(aSize);
	test(pB!=NULL);

	TInt available=pB->Avail();
	test(available==aSize);

	TInt i=0;
	TInt j=0;
	TInt k=1;
	while(k<46)
		{
		for(j=0;j<k;j++)
			{
			pB->AllocAt(i+j);
			test(!pB->IsFree(i+j));
			}
		i+=2*k;
		k++;
		}

	TInt ret=KErrNone;
	TInt pageNo=0;
	for(i=1;i<45;i++)
		{
		ret=pB->ExtractRamPages(i,pageNo);	//look for a gap of size i pages and allocate it
		test(pageNo==i*i);				//test the right page no is returned
		test.Printf(_L("OK  -pageNo is :%d\r\n"),pageNo);
		for(j=i*i;j<i*i + i;j++)		//test that the pages are actually allocated
			test(!pB->IsFree(j));
		}

	ret=pB->ExtractRamPages(45,pageNo);//there's not a big enough space in the bitmap for this to succeed
	test(ret==KErrNoMemory);
	delete pB;
	test.End();
	}

LOCAL_C void testAll(TInt aSize)
//
//	Test all BMA functions using a BMA of size aSize
//
	{

	TBuf<0x40> b;
	b.Format(_L("BitMapAllocator size = %d"),aSize);
	test.Start(b);
//
	testNew(aSize);
	testNewL(aSize);
	testAlloc(aSize);
	testBlock(aSize);
//
	test.End();
	}

GLDEF_C TInt E32Main()
//
// Test bitmap allocator
//
	{
	test.Title();
	__UHEAP_MARK;
//
	test.Start(_L("1 bit"));
	testAll(1);

	test.Next(_L("4 bit"));
	testAll(4);
//
	test.Next(_L("32 bit"));
	testAll(32);
//
	test.Next(_L("33 bit"));
	testAll(33);
//
	test.Next(_L("68 bit"));
	testAll(68);
//
	test.Next(_L("96 bit"));
	testAll(96);
//
	test.Next(_L("64 bit"));
	testAll(64);
//
	test.Next(_L("65 bit"));
	testAll(65);
//
	test.Next(_L("63 bit"));
	testAll(63);

	testContiguousAllocation(2048);

	test.Next(_L("test defect EXT-5AMDKP"));

	CBitMapAllocator* pB = CBitMapAllocator::New(64);
	test(pB != NULL);
	pB->AllocAt(0, 32);
	pB->Free(2, 2);
	pB->Free(5, 2);
	pB->Free(8, 2);
	pB->Free(12, 3);
	pB->Free(30, 2);
	TInt page = -1;
	pB->ExtractRamPages(3, page);
	test(page == 12);
	pB->ExtractRamPages(4, page);
	test(page == 30);
	pB->ExtractRamPages(1, page);
	test(page == 2);
	pB->ExtractRamPages(5, page);
	test(page == 34);
	pB->ExtractRamPages(2, page);
	test(page == 5);
	delete pB;	
		
	__UHEAP_MARKEND;
	test.End();
	return(KErrNone);
	}

