kerneltest/e32test/mmu/t_chunk.cpp
changeset 0 a41df078684a
child 4 56f325a607ea
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/mmu/t_chunk.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1234 @@
+// 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\mmu\t_chunk.cpp
+// Overview:
+// Test RChunk class
+// API Information:
+// RChunk, RChangeNotifier, TFindChunk
+// Details:
+// - Test adjusting chunks: create a global chunk, adjust it, do some size 
+// checks, verify Create() rounds up to chunk multiple, verify that adjust
+// request for size greater than MaxSize returns an error.
+// - Test creating chunks with small sizes (0 and 1), verify results are as expected.
+// - Create multiple local and global chunks, verify that Size,MaxSize,Name & FullName methods 
+// are as expected. 
+// Check Create method of global chunk if the name is already in use. Check the name can be 
+// reused after the chunk is closed.
+// - Perform adjust tests on a chunk, verify results are as expected.
+// - Open and test global chunks with multiple references, verify results are as expected.
+// - Open and test local chunks, check TFindChunk::Next method, verify results are as expected.
+// - Check user thread is panicked if it creates or adjusts chunk with negative size or of an
+// invalid type or with an invalid name.
+// - Check the chunk is filled in with the appropriate clear value after creation, verify results
+// are as expected.
+// - Test sharing a chunk between threads, verify results are as expected.
+// - Create a thread to watch for notification of changes in free memory and
+// changes in out of memory status. Verify adjusting an RChunk generates
+// the expected notifications.
+// - Test finding a global chunk by name and verify results are as expected.
+// Platforms/Drives/Compatibility:
+// All.
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+// 
+//
+
+#define __E32TEST_EXTENSION__
+
+#include <e32test.h>
+#include <e32panic.h>
+#include <e32svr.h>
+#include "mmudetect.h"
+#include "d_gobble.h"
+#include "freeram.h"
+
+const TInt KHeapSize=0x200;
+
+//const TInt KNumberOfChunks=10; can't have that many
+const TInt KNumberOfChunks=3;
+
+const TInt KChunkNum=5;
+const TInt KNormalReturn=194;
+
+#ifdef __WINS__
+const TInt KMinChunkSizeInBytesMinus1=0x0000ffff;
+const TUint KMinChunkSizeInBytesMask=0xffff0000;
+#elif defined (__X86__)
+const TInt KMinChunkSizeInBytesMinus1=0x003fffff;
+const TUint KMinChunkSizeInBytesMask=0xffc00000;
+#else
+const TInt KMinChunkSizeInBytesMinus1=0x000fffff;
+const TUint KMinChunkSizeInBytesMask=0xfff00000;
+#endif
+
+const TInt KMinPageSizeInBytesMinus1=0x00000fff;
+const TUint KMinPageSizeInBytesMask=0xfffff000;
+TInt gPageSize;
+
+LOCAL_D RTest test(_L("T_CHUNK"));
+LOCAL_D RTest t(_L("ShareThread"));
+LOCAL_D RChunk gChunk;
+
+LOCAL_D TPtr nullPtr(NULL,0);
+
+TUint32 MemModel;
+
+enum TDirective
+	{
+	ENormal,
+	ECreateNegative,
+	EAdjustNegative,
+	ECreateInvalidType,
+	ECreateInvalidName,
+	ECreateNoName,
+	};
+
+const TUint8 KDfltClearByte = 0x3;
+TBool CheckChunkCleared(RChunk& aRC, TInt aOffset=0, TUint8 aClearByte = KDfltClearByte)
+	{
+	TUint8* base = aRC.Base()+aOffset;
+	TInt size = aRC.Size();
+	test.Printf(_L("Testing chunk for 0x%x - size: %d!\n"), aClearByte, size);
+	TBool ret=ETrue;
+	for(TInt i = 0; i<size; i++)
+		if(base[i] != aClearByte)
+			ret=EFalse;
+	memset((TAny*)base, 0x05, size);
+	return ret;
+	}
+
+TInt roundToPageSize(TInt aSize)
+	{
+
+	return(((aSize+KMinPageSizeInBytesMinus1)&KMinPageSizeInBytesMask));
+	}
+
+TInt roundToChunkSize(TInt aSize)
+	{
+	if(MemModel==EMemModelTypeFlexible)
+		return roundToPageSize(aSize);
+	return(((aSize+KMinChunkSizeInBytesMinus1)&KMinChunkSizeInBytesMask));
+	}
+
+TInt ThreadEntry2(TAny* /*aParam*/)
+//
+//	Thread to read from shared chunk
+//
+	{
+	RChunk c;
+	TInt r;
+	r=c.OpenGlobal(_L("Marmalade"),ETrue);
+	t(r==KErrNone);
+	TUint8* base=c.Base();
+	for (TInt8 i=0;i<10;i++)
+		t(*base++==i); // check the chunk has 0-9
+	c.Close();
+	return(KErrNone);
+	}
+
+TInt ThreadEntry(TAny* aDirective)
+//
+//	Thread to create a Panic in a variety of ways
+//
+	{
+
+	switch((TUint)aDirective)
+		{
+	case ENormal:
+		{
+		return KNormalReturn;
+		}
+	case ECreateNegative:
+		{
+		gChunk.CreateLocal(0x10,-0x10);
+		test(EFalse); 
+		}
+	case EAdjustNegative:
+		{
+		gChunk.Adjust(-0x10);
+		test(EFalse);
+		}
+	case ECreateInvalidType :
+		{
+		TChunkCreateInfo createInfo;
+		createInfo.SetCode(gPageSize, gPageSize);
+		_LIT(KChunkName, "Chunky");
+		createInfo.SetGlobal(KChunkName);
+		RChunk chunk;
+		chunk.Create(createInfo);
+		test(EFalse);
+		}
+	default:
+		test(EFalse);
+		}
+	return(KErrNone);
+	}
+
+//
+//	Test the clear flags for the specified chunk.
+//	Assumes chunk has one commited region from base to aBytes.
+//
+void TestClearChunk(RChunk& aChunk, TUint aBytes, TUint8 aClearByte)
+	{
+	test_Equal(ETrue, CheckChunkCleared(aChunk, 0, aClearByte));
+	test_KErrNone(aChunk.Adjust(0));
+	test_KErrNone(aChunk.Adjust(aBytes));
+	test_Equal(ETrue, CheckChunkCleared(aChunk, 0, aClearByte));
+	aChunk.Close();
+	}
+
+
+//
+// Create the specified thread and verify the exit reason.
+//
+void TestThreadExit(TExitType aExitType, TInt aExitReason, TThreadFunction aFunc, TAny* aThreadParam)
+	{
+	RThread thread;
+	test_KErrNone(thread.Create(_L("RChunkPanicThread"), aFunc, KDefaultStackSize, 
+								KHeapSize, KHeapSize, aThreadParam));
+	// Disable JIT debugging.
+	TBool justInTime=User::JustInTime();
+	User::SetJustInTime(EFalse);
+
+	TRequestStatus status;
+	thread.Logon(status); 
+	thread.Resume();
+	User::WaitForRequest(status);
+	test_Equal(aExitType, thread.ExitType());
+	test_Equal(aExitReason, status.Int());
+	test_Equal(aExitReason, thread.ExitReason());
+	if (aExitType == EExitPanic)
+		test(thread.ExitCategory()==_L("USER"));
+	CLOSE_AND_WAIT(thread);
+
+	// Put JIT debugging back to previous status.
+	User::SetJustInTime(justInTime);
+	}
+
+
+//
+// Thread function to create a chunk with invalid attributes
+//
+TInt ChunkPanicThread(TAny* aCreateInfo)
+	{
+	// This should panic.
+	RChunk chunk;
+	chunk.Create((*(TChunkCreateInfo*) aCreateInfo));
+	test(EFalse);
+
+	return KErrGeneral;	// Shouldn't reach here.
+	}
+
+void testInitialise()
+	{
+	test.Next(_L("Load gobbler LDD"));
+	TInt r = User::LoadLogicalDevice(KGobblerLddFileName);
+	test(r==KErrNone || r==KErrAlreadyExists);
+
+	// get system info...
+	MemModel = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL) & EMemModelTypeMask;
+
+	test_KErrNone(UserHal::PageSizeInBytes(gPageSize));
+	}
+
+
+void test1()
+//
+//	Test creating chunks with silly sizes
+//
+	{
+
+	__KHEAP_MARK;
+	RChunk chunk;
+	TInt r;
+	test.Start(_L("Create global chunks"));
+	r=chunk.CreateGlobal(_L("Fopp"),1,1);
+	test(r==KErrNone);
+	chunk.Close();
+	r=chunk.CreateGlobal(_L("Fopp"),0,0);
+	test(r==KErrArgument);
+
+	__KHEAP_CHECK(0);
+
+	test.Next(_L("Create local chunks"));	  
+	r=chunk.CreateLocal(1,1);
+	test(r==KErrNone);
+	chunk.Close();
+	r=chunk.CreateLocal(0,0);
+	test(r==KErrArgument);
+	test.End();
+	__KHEAP_MARKEND;
+	}
+
+
+void test2(TInt aSize)
+//
+//	Test local/global chunk creation
+//
+	{
+
+	RChunk lchunk[KNumberOfChunks];
+	RChunk gchunk[KNumberOfChunks];
+	RChunk chunk;
+
+	__KHEAP_MARK;
+	test.Start(_L("Create multiple local and global chunks"));
+
+	TInt i;
+	TBuf<0x40> name;
+	TFullName fullname;
+	for (i=0; i<KNumberOfChunks; i++)
+		{
+		TInt r;
+		// create a local chunk
+		r=lchunk[i].CreateLocal(aSize,aSize);
+		test(r==KErrNone);
+		r=lchunk[i].MaxSize();
+		test(r==roundToChunkSize(aSize));
+		test(lchunk[i].Size()==roundToPageSize(aSize)); // was 0
+		test(lchunk[i].Name().Left(6)==_L("Local-"));
+
+		fullname=RProcess().Name();
+		fullname+=_L("::");
+		fullname+=lchunk[i].Name();
+		test(lchunk[i].FullName().CompareF(fullname)==0);
+
+		// create a global chunk
+		name.Format(_L("Chunk%d"),i);
+		r=gchunk[i].CreateGlobal(name,aSize,aSize);
+		test(r==KErrNone);
+  		test(gchunk[i].MaxSize()==roundToChunkSize(aSize));
+		test(gchunk[i].Size()==roundToPageSize(aSize)); // was 0
+		test(gchunk[i].Name()==name);
+		}
+
+	// make room for another chunk
+	lchunk[KNumberOfChunks-1].Close();
+	gchunk[KNumberOfChunks-1].Close();
+
+//	Try to create a global chunk with a duplicate name
+	test.Next(_L("Create a chunk with a duplicate name"));
+	TInt r=chunk.CreateGlobal(_L("Chunk0"),aSize,aSize);
+	test(r==KErrAlreadyExists);
+
+ 	test.Next(_L("Close all chunks"));
+	for (i=0; i<KNumberOfChunks-1; i++)
+		{
+		lchunk[i].Close();
+		gchunk[i].Close();
+		}
+
+	test.Next(_L("Reuse a name"));
+	r=chunk.CreateGlobal(_L("Chunk1"),aSize,aSize);
+	test(r==KErrNone);
+	test(chunk.MaxSize()==roundToChunkSize(aSize));
+   	test(chunk.Size()==roundToPageSize(aSize));	
+	test(chunk.Name()==_L("Chunk1"));
+  	chunk.Close();
+
+	test.End();
+	__KHEAP_MARKEND;
+	}
+
+void test3_2(RChunk& aChunk, TInt aSize)
+//
+//	Perform Adjust tests on aChunk
+//
+	{
+
+	TInt r;
+	test.Start(_L("Adjust to full size"));
+	r=aChunk.Adjust(aSize);
+ 	test(r==KErrNone);
+	test(aChunk.Size()==roundToPageSize(aSize));
+	test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+	test.Next(_L("Adjust a chunk to half size"));
+	r=aChunk.Adjust(aSize/2);
+	test(r==KErrNone);
+	test(aChunk.Size()==roundToPageSize(aSize/2));
+	test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+	test.Next(_L("Adjust to same size"));
+	r=aChunk.Adjust(aSize/2);
+	test(r==KErrNone);
+	test(aChunk.Size()==roundToPageSize(aSize/2));
+	test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+	test.Next(_L("Adjusting to size=0"));
+ 	r=aChunk.Adjust(0);
+	test(r==KErrNone);
+	test(aChunk.Size()==0);
+	test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+	test.Next(_L("Adjust back to half size"));
+	r=aChunk.Adjust(aSize/2);
+	test(r==KErrNone);
+	test(aChunk.Size()==roundToPageSize(aSize/2));
+	test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+	test.End();
+	}
+
+
+void test3(TInt aSize)
+//
+//	Test Adjust size of chunk	
+//
+	{
+
+	RChunk chunk;
+	__KHEAP_MARK;	
+	TInt r;
+//	Run Adjust tests with a local chunk
+	test.Start(_L("Local Chunk.Adjust()"));
+	r=chunk.CreateLocal(aSize,aSize);
+	test3_2(chunk, aSize);
+	chunk.Close();
+//	Run Adjust tests with a global chunk
+	test.Next(_L("Global Chunk.Adjust()"));
+	r=chunk.CreateGlobal(_L("Fopp"),aSize,aSize);
+	test(KErrNone==r);
+	test3_2(chunk,aSize);
+	chunk.Close();
+	test.End();
+	__KHEAP_MARKEND;
+	}
+
+void test4(TInt aSize)
+//
+//	Test OpenGlobal
+//
+	{
+
+	RChunk chunk[KChunkNum];
+	RChunk c1, c2, c3;
+	TName name;
+
+	__KHEAP_MARK;
+	TInt i,r;
+	for (i=0; i<KChunkNum; i++)
+		{
+		name.Format(_L("Chunk%d"), i);
+		r=chunk[i].CreateGlobal(name,aSize,aSize);
+		}
+
+	test.Start(_L("Open Global chunks"));
+	r=c1.OpenGlobal(_L("Chunk1"),EFalse); // 2nd ref to this chunk
+	test(r==KErrNone);
+	r=c2.OpenGlobal(_L("Chunk3"),EFalse); // 2nd ref to this chunk
+	test(r==KErrNone);
+	r=c3.OpenGlobal(_L("Chunk4"),EFalse); // 2nd ref to this chunk
+	test(r==KErrNone);
+	c3.Close();
+
+	test.Next(_L("Attempt Open non-existant chunk"));
+	r=c3.OpenGlobal(_L("Non Existant Chunk"),EFalse);
+	test(r==KErrNotFound);
+
+	for (i=0; i<KChunkNum; i++)
+		{
+		test.Printf(_L("Closing chunk %d\n"),i);
+		chunk[i].Close();
+		}
+
+	test.Next(_L("Test chunks with multiple references are still valid"));
+	r=c1.Adjust(aSize/2);
+	test(r==KErrNone);
+	test(c1.MaxSize()==roundToChunkSize(aSize));
+	test(c1.Size()==roundToPageSize(aSize/2));
+	test(c1.Name()==_L("Chunk1"));
+
+	r=c2.Adjust(aSize/2);
+	test(r==KErrNone);
+	test(c2.MaxSize()==roundToChunkSize(aSize));
+	test(c2.Size()==roundToPageSize(aSize/2));
+	test(c2.Name()==_L("Chunk3"));
+
+//	Open another reference to a chunk
+	r=c3.OpenGlobal(_L("Chunk3"),EFalse);
+	test(r==KErrNone);
+	test(c3.Base()==c2.Base());
+	test(c3.Size()==c2.Size());
+	test(c2.Size()!=aSize);
+//	Adjust with one reference
+	r=c3.Adjust(aSize);
+	test(r==KErrNone);
+//	Test sizes from the other
+	test(c2.Size()==roundToPageSize(aSize));
+	test(c2.MaxSize()==roundToChunkSize(aSize));
+
+	c1.Close();
+	c2.Close();
+	c3.Close();
+
+	test.Next(_L("And check the heap..."));
+	test.End();
+	__KHEAP_MARKEND;
+	}
+
+void test5(TInt aSize)
+//
+//	Test Open
+//
+	{
+	
+	RChunk chunk[2*KNumberOfChunks];
+	RChunk c1;
+
+	test.Start(_L("Creating Local and Global Chunks"));
+	__KHEAP_MARK;
+	TInt i,r;
+	TBuf<0x40> b;
+
+//	Create KNumberOfChunks Global chunks
+	for (i=0; i<KNumberOfChunks; i++)
+		{
+		b.Format(_L("Chunk%d"), i);
+		r=chunk[i].CreateGlobal(b,aSize,aSize);
+		test(chunk[i].Name()==b);
+		test(r==KErrNone);
+		
+		b.Format(_L("This is chunk %d"), i);
+		b.Append(TChar(0));
+		chunk[i].Adjust(aSize);
+		Mem::Copy(chunk[i].Base(), b.Ptr(), b.Size());
+		test(chunk[i].MaxSize()==roundToChunkSize(aSize));
+		test(chunk[i].Size()==roundToPageSize(aSize));
+		}
+
+	test.Next(_L("Find and Open the Chunks"));
+	TFindChunk find;
+	TFullName name;
+	for (i=0; i<KNumberOfChunks; i++)
+		{
+		test.Printf(_L("Opening chunk %d\n"),i);
+		find.Find(chunk[i].FullName());
+		r = find.Next(name);
+		test(r==KErrNone);
+		c1.Open(find);
+		b=TPtrC((TText*)c1.Base());
+		name.Format(_L("This is chunk %d"), i);
+		test(b==name);
+		c1.Close();
+		}
+	
+	test.Next(_L("Close chunks"));
+	for (i=0; i<KNumberOfChunks; i++)
+		chunk[i].Close();
+
+	test.End();
+	__KHEAP_MARKEND;
+	}
+
+
+void test7(TInt aSize)
+//
+//	Deliberately cause RChunk panics
+//
+	{
+	__KHEAP_MARK;
+
+//	ENormal
+	test.Start(_L("Test panic thread"));
+	TestThreadExit(EExitKill, KNormalReturn, ThreadEntry, (TAny*)ENormal);
+
+//	ECreateNegative
+	test.Next(_L("Create Chunk with a negative size"));
+	TestThreadExit(EExitPanic, EChkCreateMaxSizeNegative, ThreadEntry, (TAny*) ECreateNegative);
+
+//	EAdjustNegative
+	test.Next(_L("Adjust a Chunk to Size = -0x10"));
+	gChunk.CreateLocal(aSize,aSize);
+	TestThreadExit(EExitPanic, EChkAdjustNewSizeNegative, ThreadEntry, (TAny*) EAdjustNegative);
+	gChunk.Close();
+
+// ECreateInvalidType
+	test.Next(_L("Create chunk of invalid type"));
+	TestThreadExit(EExitPanic, EChkCreateInvalidType, ThreadEntry, (TAny*) ECreateInvalidType);
+
+	test.End();
+
+	__KHEAP_MARKEND;
+	}
+
+
+void testClear(TInt aSize)
+	{
+	__KHEAP_MARK;
+	test.Start(_L("Test clearing memory (Platform Security)"));
+	
+	RChunk c1,c2,c3,c4,c5,c6,c7,c8,c9,c10;
+	TInt r;
+	
+	TBuf<0x40> b;
+	
+	b.Copy(_L("Chunk"));
+	
+	r=c1.CreateGlobal(b,aSize,aSize);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c1));
+	c1.Close();
+	
+	r=c2.CreateLocal(aSize,aSize,EOwnerProcess);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c2));
+	c2.Close();
+
+	r=c3.CreateLocalCode(aSize,aSize,EOwnerProcess);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c3));
+	c3.Close();
+
+	r=c4.CreateDoubleEndedLocal(0x1000,0x1000+aSize,0x100000);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c4,c4.Bottom()));
+	c4.Close();
+
+	r=c5.CreateDoubleEndedGlobal(b,0x1000,0x1000+aSize,0x100000,EOwnerProcess);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c5,c5.Bottom()));
+	c5.Close();
+
+	r=c6.CreateDisconnectedLocal(0x1000,0x1000+aSize,0x100000);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c6,0x1000));
+	c6.Close();
+
+	r=c7.CreateDisconnectedGlobal(b,0x1000,0x1000+aSize,0x100000,EOwnerProcess);
+	test(r==KErrNone);
+	
+	test((TBool)ETrue==CheckChunkCleared(c7,0x1000));
+	c7.Close();
+
+	test.Next(_L("Test setting the clear byte of RChunk::Create()"));
+
+	TChunkCreateInfo createInfo;
+	createInfo.SetNormal(aSize, aSize);
+	test_KErrNone(c10.Create(createInfo));
+	TestClearChunk(c10, aSize, KDfltClearByte);
+
+	createInfo.SetClearByte(0x0);
+	test_KErrNone(c8.Create(createInfo));
+	TestClearChunk(c8, aSize, 0x0);
+
+	createInfo.SetClearByte(0xff);
+	test_KErrNone(c9.Create(createInfo));
+	TestClearChunk(c9, aSize, 0xff);
+
+	test.End();
+	__KHEAP_MARKEND;
+	}
+
+void testShare()
+//
+// Test sharing a chunk between threads
+//
+	{
+	test.Start(_L("Test chunk sharing between threads"));
+
+	test.Next(_L("Create chunk Marmalade"));
+	TInt r=0;
+	RChunk chunk;
+	TInt size=0x1000;
+	TInt maxSize=0x5000;
+	r=0;
+	r=chunk.CreateGlobal(_L("Marmalade"),size,maxSize);
+	test(r==KErrNone);
+	test.Next(_L("Write 0-9 to it"));
+	TUint8* base=chunk.Base();
+	for (TInt8 j=0;j<10;j++)
+		*base++=j; // write 0 - 9 to the chunk
+
+	RThread t;
+	TRequestStatus stat;
+	test.Next(_L("Create reader thread"));
+	r=t.Create(_L("RChunkShareThread"), ThreadEntry2, KDefaultStackSize,KHeapSize,KHeapSize,NULL);
+	test(r==KErrNone);
+	t.Logon(stat);
+	test.Next(_L("Resume reader thread"));
+	t.Resume();
+	User::WaitForRequest(stat);	
+	CLOSE_AND_WAIT(t);
+	chunk.Close();
+
+	test.End();
+	}
+
+void FindChunks()
+    { // taken from some code written by SteveG
+    test.Start(_L("Finding chunks...\n"));
+
+    TFullName name=_L("*");
+    TFindChunk find(name);
+	TInt i=0;
+
+
+    while (find.Next(name)==KErrNone)
+        {
+        RChunk chunk;
+        test.Printf(_L("Chunk name %S\n"),&name);
+		TInt err=chunk.Open(find);
+        if (err)
+            test.Printf(_L("Error %d opening chunk"),err);
+        else
+        	{
+			TBuf<16> access;
+			if (chunk.IsWritable())
+				access=_L("ReadWrite");
+			else if (chunk.IsReadable())
+				access=_L("ReadOnly");
+			else
+				access=_L("No Access");
+            test.Printf(_L("Chunk size %08x bytes, %S\n"),chunk.Size(),&access);
+			chunk.Close();
+			i++;
+			}
+        User::After(1000000);
+  	    }
+    test.End();
+    }
+
+void testAdjustChunk()
+	{
+	test.Start(_L("Test adjusting chunks"));
+
+	RChunk hermione;
+	
+	test.Next(_L("Create global chunk"));
+	TInt r=hermione.CreateGlobal(_L("Hermione"),0x1000,0x100000);
+	test(r==KErrNone);
+	TUint32* base=(TUint32*)hermione.Base();
+	TUint32* top=(TUint32*)(hermione.Base()+hermione.Size());
+	TUint32* i;
+
+	test.Printf(_L("Base = %08x, Top = %08x\n"),base,top);
+	test.Next(_L("Check I can write to all of it"));
+	for (i=base;i<top;i++)
+		*i=0xdeaddead;
+
+	test.Next(_L("Adjust the chunk"));
+	r=hermione.Adjust(0x1400);
+	test(r==KErrNone);
+
+	base=(TUint32*)hermione.Base();
+	top=(TUint32*)(hermione.Base()+hermione.Size());
+	test.Printf(_L("Base = %08x, Top = %08x\n"),base,top);
+	test.Next(_L("Check I can write to all of the bigger chunk"));
+	for (i=base;i<top;i++)
+		*i=0xdeaddead;
+
+	hermione.Close();
+
+	test.Next(_L("Do some size checks"));
+	RChunk wibble;
+	r=wibble.CreateGlobal(_L("Wibble"),0x1,gPageSize*8);
+	test(r==KErrNone);
+	test.Next(_L("Check create rounds up to page multiple"));
+	test(wibble.Size()==(TInt)gPageSize);
+	test.Next(_L("Check create rounds up to chunk multiple"));
+	test(wibble.MaxSize()==roundToChunkSize(gPageSize*8));
+
+	test.Next(_L("Check adjust rounds up to page multiple"));
+	r=wibble.Adjust((gPageSize*6)-12);
+	test(r==KErrNone);
+	test(wibble.Size()==gPageSize*6);
+
+	test.Next(_L("Different number, same size"));
+	r=wibble.Adjust((gPageSize*6)-18);
+	test(r==KErrNone);
+	test(wibble.Size()==gPageSize*6);
+
+	test.Next(_L("Check adjust > MaxSize returns error"));
+	r=wibble.Adjust(wibble.MaxSize()+gPageSize);
+	test(r==KErrArgument);
+
+	wibble.Close();
+	test.End();
+	}
+
+TInt NotifierCount=0;
+TInt OOMCount=0;
+RChangeNotifier Notifier;
+RThread NtfThrd;
+_LIT(KNotifierThreadName,"NotifierThread");
+
+TInt NotifierThread(TAny*)
+	{
+	TInt r=Notifier.Create();
+	while (r==KErrNone)
+		{
+		TRequestStatus s;
+		r=Notifier.Logon(s);
+		if (r!=KErrNone)
+			break;
+		User::WaitForRequest(s);
+		if (s.Int()&EChangesFreeMemory)
+			++NotifierCount;
+		if (s.Int()&EChangesOutOfMemory)
+			++OOMCount;
+		}
+	Notifier.Close();
+	return r;
+	}
+
+
+void WaitForNotifier()
+	{
+	User::After(500000);		// wait for notifier
+	}
+
+
+void CheckNotifierCount(TInt aLevel, TInt aOom)
+	{
+	WaitForNotifier();
+	if (NtfThrd.ExitType()!=EExitPending)
+		{
+		TExitCategoryName exitCat=NtfThrd.ExitCategory();
+		test.Printf(_L("Thread exited: %d,%d,%S"),NtfThrd.ExitType(),NtfThrd.ExitReason(),&exitCat);
+		test(0);
+		}
+	TInt c1=NotifierCount;
+	TInt c2=OOMCount;
+	if (c1!=aLevel || c2!=aOom)
+		{
+		test.Printf(_L("Count %d,%d Expected %d,%d"),c1,c2,aLevel,aOom);
+		test(0);
+		}
+	}
+
+void testNotifiers()
+	{
+	RGobbler gobbler;
+	TInt r = gobbler.Open();
+	test(r==KErrNone);
+	TUint32 taken = gobbler.GobbleRAM(128*1024*1024);
+	test.Printf(_L("Gobbled: %dK\n"), taken/1024);
+	test.Printf(_L("Free RAM 0x%08X bytes\n"),FreeRam());
+
+	test.Next(_L("Create thread"));
+	r=NtfThrd.Create(KNotifierThreadName,NotifierThread,KDefaultStackSize,NULL,NULL);
+	test(r==KErrNone);
+	NtfThrd.SetPriority(EPriorityMore);
+	NtfThrd.Resume();
+	test.Next(_L("Check for initial notifier"));
+	CheckNotifierCount(1,1);
+	TInt free=FreeRam();
+	test.Printf(_L("Free RAM: %dK\n"),free/1024);
+	test(free>=1048576);
+	test.Next(_L("Set thresholds"));
+	r=UserSvr::SetMemoryThresholds(65536,524288);	// low=64K good=512K
+	test(r==KErrNone);
+	test.Next(_L("Create chunk"));
+	// Chunk must not be paged otherwise it will not effect the amount 
+	// of free ram reported plus on h4 swap size is less than the total ram.
+	TChunkCreateInfo createInfo;
+	createInfo.SetNormal(0, free+2097152);
+	createInfo.SetPaging(TChunkCreateInfo::EUnpaged);
+	RChunk c;
+	test_KErrNone(c.Create(createInfo));
+	const TInt KBufferSpace = 768*1024;	// 768K buffer
+	CheckNotifierCount(1,1);
+	test.Next(_L("Leave 768K"));
+	r=c.Adjust(free-KBufferSpace);	// leave 768K
+	test(r==KErrNone);
+	CheckNotifierCount(1,1);		// shouldn't get notifier
+	TInt free2=FreeRam();
+	test.Printf(_L("Free RAM: %dK\n"),free2/1024);
+	test(free2<=KBufferSpace);
+	TInt free3=free-(KBufferSpace-free2);	// this accounts for space used by page tables
+	test.Next(_L("Leave 32K"));
+	r=c.Adjust(free3-32768);		// leave 32K
+	test(r==KErrNone);
+	CheckNotifierCount(2,1);		// should get notifier
+	test.Next(_L("Leave 28K"));
+	r=c.Adjust(free3-28672);		// leave 28K
+	test(r==KErrNone);
+	CheckNotifierCount(2,1);		// shouldn't get another notifier
+	test.Next(_L("Ask for too much"));
+	r=c.Adjust(free3+4096);			// try to get more than available
+	test(r==KErrNoMemory);
+	CheckNotifierCount(2,2);		// should get another notifier
+	test.Next(_L("Leave 128K"));
+	r=c.Adjust(free3-131072);		// leave 128K
+	test(r==KErrNone);
+	CheckNotifierCount(2,2);		// shouldn't get another notifier
+	test.Next(_L("Leave 640K"));
+	r=c.Adjust(free3-655360);		// leave 640K
+	test(r==KErrNone);
+	CheckNotifierCount(3,2);		// should get another notifier
+	test.Next(_L("Leave 1M"));
+	r=c.Adjust(free3-1048576);		// leave 1M
+	test(r==KErrNone);
+	CheckNotifierCount(3,2);		// shouldn't get another notifier
+	test.Next(_L("Ask for too much"));
+	r=c.Adjust(free3+4096);			// try to get more than available
+	test(r==KErrNoMemory);
+
+	TInt notifierCount = 3;
+	if(MemModel==EMemModelTypeFlexible)
+		{
+		// on flexible memory model, we get memory changed notifiers
+		// on failed memory allocation; this hack lets the test code
+		// pass this as acceptable behaviour...
+		WaitForNotifier();
+		notifierCount = NotifierCount; // expect whatever we actually got
+		}
+
+	CheckNotifierCount(notifierCount,3);		// should get another notifier
+	test.Next(_L("Leave 1M"));
+	r=c.Adjust(free3-1048576);					// leave 1M
+	test(r==KErrNone);
+	CheckNotifierCount(notifierCount,3);		// shouldn't get another notifier
+
+	c.Close();
+	TRequestStatus s;
+	NtfThrd.Logon(s);
+	NtfThrd.Kill(0);
+	User::WaitForRequest(s);
+	CLOSE_AND_WAIT(NtfThrd);
+	Notifier.Close();
+	gobbler.Close();
+	}
+
+
+// TestFullAddressSpace is used to stress the memory allocation mechanism(beyond the 1GB limit).
+// However, the memory model can introduce limitations in the total amount of memory a single 
+// process is allowed to allocate. To make the test more generic before closing the reserved 
+// chunks for this test we trigger the creation of a new process. This process executes 
+// t_chunk again, passing argument "extended". The result is that more chunks will be created
+// through another call to TestFullAddressSpace, with the parameter extendedFlag set to true. 
+// Eventually the total amount of allocated space will overcome the 1Gb limit in any case.
+
+void TestFullAddressSpace(TBool extendedFlag )
+	{
+	test.Start(_L("Fill process address space with chunks\n"));
+	RChunk chunk[2][11];
+	TInt total = 0;
+	TInt i;
+	TInt j;
+	TInt r;
+		
+	for(j=0; j<=1; j++)
+		{
+		if(!j)
+			test.Next(_L("Creating local chunks"));
+		else
+			test.Next(_L("Creating global chunks"));
+		for(i=10; i>=0; --i)
+			{
+			TInt size = 1<<(20+i);
+			
+			if(!j)
+				r = chunk[j][i].CreateDisconnectedLocal(0,0,size);
+			else
+				r = chunk[j][i].CreateDisconnectedGlobal(KNullDesC,0,0,size);
+			TBuf<128> text;
+			text.AppendFormat(_L("Create %dMB chunk returns %d"),1<<i,r);
+			test.Next(text);
+			if(r!=KErrNoMemory)
+				{
+				test(r==KErrNone);
+				// commit memory to each 1MB region,
+				// this excercises page table allocation
+				volatile TUint8* base = (TUint8*)chunk[j][i].Base();
+				for(TInt o=0; o<size; o+=1<<20)
+					{
+					r = chunk[j][i].Commit(o,1);
+					test(r==KErrNone);
+					// access the commited memory...
+					base[o] = (TUint8)(o&0xff);
+					test(base[o]==(TUint8)(o&0xff));
+					base[o] = (TUint8)~(o&0xff);
+					test(base[o]==(TUint8)~(o&0xff));
+					}
+				total += 1<<i;
+				}
+			}
+		}
+		
+	if (extendedFlag == EFalse)
+		{
+		
+		test.Printf(_L("Total chunk size created was %d MB\n\n"),total);	  
+
+		if(total<1024)
+			{
+			_LIT(KOtherProcessName,"t_chunk");
+			_LIT(KProcessArgs,"extended");
+			
+			RProcess process;      
+			r=process.Create(KOtherProcessName,KProcessArgs );
+			test.Printf(_L("Creating new process( t_chunk extended) returns %d\n"),r );
+			test( r == KErrNone);
+		   
+			TRequestStatus status;
+			process.Logon(status);
+			process.Resume(); 
+			
+			User::WaitForRequest(status);
+			  
+			test(process.ExitType() == EExitKill);
+			test(process.ExitReason() == 0);
+			process.Close();
+			}
+	
+		}           
+  	else
+  		test.Printf(_L("Total chunk size created by the new process was %d MB\n"),total); 
+ 
+	for(j=0; j<=1; j++)
+		for(i=10; i>=0; --i)
+			chunk[j][i].Close();
+	test.End();
+	}	
+
+
+#ifdef __WINS__
+void TestExecLocalCode()
+	{
+	RChunk c;
+	TInt size = 10 * 1024;
+	TInt rc = c.CreateLocalCode(size, size, EOwnerProcess);
+	test_KErrNone(rc);
+	TUint8 *p = c.Base();
+	TUint32 (*func)() = (TUint32 (*)())p;
+	test.Printf(_L("Create small function in the new code chunk\n"));
+	*p++ = 0xB8;		// mov eax, 0x12345678
+	*p++ = 0x78;
+	*p++ = 0x56;
+	*p++ = 0x34;
+	*p++ = 0x12;
+	*p   = 0xC3;		// ret
+	test.Printf(_L("Going to call the new function\n"));
+	TUint32 res = (*func)();
+	test_Equal(0x12345678, res);
+	c.Close();
+	}
+#endif	//  __WINS__
+
+
+_LIT(KChunkName, "CloseChunk");
+
+struct TRequestData
+	{
+	RSemaphore requestSem;
+	RSemaphore completionSem;
+	RSemaphore nextItSem;
+	};
+
+TInt CloseThread(TAny* data)
+	{
+	TRequestData* reqData = (TRequestData*)data;
+	ASSERT(reqData);
+	
+	for(;;)
+		{
+		// Wait for a request to open and close the chunk.
+		reqData->requestSem.Wait();
+		
+		// Try to open the chunk (may have already been closed by another thread).
+		RChunk chunk;
+		TInt r = chunk.OpenGlobal(KChunkName, EFalse, EOwnerThread);
+		if (r != KErrNone)
+			{
+			// The chunk was already closed...
+			r = (r == KErrNotFound) ? KErrNone : r;	// Ensure no debug output for expected failures.
+			
+			if(r != KErrNone)
+				{
+				test.Printf(_L("CloseThread RChunk::OpenGlobal Err: %d\n"), r);
+				test_KErrNone(r);
+				}
+			}
+		else
+			{ 
+			// Close the chunk.
+			chunk.Close();
+			}
+		
+		// Tell our parent we have completed this iteration and wait for the next.
+		reqData->completionSem.Signal();
+		reqData->nextItSem.Wait();
+		}
+	}
+
+void TestClosure()
+	{
+	const TUint KCloseThreads = 50;
+	RThread thread[KCloseThreads];
+	TRequestStatus dead[KCloseThreads];
+
+	// We need three semaphores or we risk signal stealing if one thread gets ahead of the
+	// others and starts a second iteration before the other threads have been signalled
+	// and have begun their first iteration.  Such a situation results in deadlock so we
+	// force all threads to finish the iteration first using the nextItSem semaphore to
+	// ensure we can only move to the next iteration once every thread has completed the
+	// current iteration.
+	TRequestData reqData;
+	test_KErrNone(reqData.requestSem.CreateLocal(0));
+	test_KErrNone(reqData.completionSem.CreateLocal(0));
+	test_KErrNone(reqData.nextItSem.CreateLocal(0));
+	
+	TUint i = 0;
+
+	// Create thread pool.  We do this rather than create 50 threads
+	// over and over again for 800 times - the kernel's garbage collection
+	// does not keep up and we run out of backing store.
+	for (; i < KCloseThreads; i++)
+		{
+		test_KErrNone(thread[i].Create(KNullDesC, CloseThread, KDefaultStackSize, NULL, (TAny*)&reqData));
+		thread[i].Logon(dead[i]);
+		thread[i].SetPriority(EPriorityMuchLess);
+		thread[i].Resume();
+		}
+
+	for (TUint delay = 200; delay < 1000; delay++)
+		{
+		test.Printf(_L("Closure delay %dus\r"), delay);
+
+		// Create a global chunk.
+		RChunk chunk;
+		test_KErrNone(chunk.CreateGlobal(KChunkName, gPageSize, gPageSize));
+
+		// Release the threads so they can try to close the handle.
+		reqData.requestSem.Signal(KCloseThreads);
+
+		// Wait for the delay then close the handle ourselves.
+		User::AfterHighRes(delay);
+		chunk.Close();
+
+		// Wait for the threads to complete then release them for the next iteration.
+		for (i = 0; i < KCloseThreads; i++)
+			{
+			reqData.completionSem.Wait();
+			}
+		reqData.nextItSem.Signal(KCloseThreads);
+
+		// Ensure garbage collection is complete to prevent the kernel's
+		// garbage collection from being overwhelmed and causing the
+		// backing store to be exhausted.
+		UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);
+		}
+
+	// Kill thread pool.
+	for (i = 0; i < KCloseThreads; i++)
+		{
+		thread[i].Kill(KErrNone);
+		User::WaitForRequest(dead[i]);
+		test(KErrNone == thread[i].ExitReason());
+		test_Equal(EExitKill, thread[i].ExitType());
+		thread[i].Close();
+		}
+		
+	reqData.requestSem.Close();
+	reqData.completionSem.Close();
+	reqData.nextItSem.Close();
+
+	// Ensure garbage collection is complete to prevent false positive
+	// kernel memory leaks.
+	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);
+	}
+	
+
+/**Returns true if 'extended' is found in the command line*/
+TBool GetExtended()
+	{
+	_LIT(KExtended,"extended");
+	TBuf<64> c;
+	User::CommandLine(c);
+	if (c.FindF(KExtended) >= 0)
+		return ETrue;
+	return EFalse;
+	}
+
+
+TInt E32Main()
+//
+//	Test RChunk class
+//
+	{
+	test.Title();
+	if (!HaveVirtMem())
+		{
+		test.Printf(_L("This test requires an MMU\n"));
+		return KErrNone;
+		}
+	testInitialise();
+
+	// Turn off lazy dll unloading so the kernel heap checking isn't affected.
+	RLoader l;
+	test(l.Connect()==KErrNone);
+	test(l.CancelLazyDllUnload()==KErrNone);
+	l.Close();
+
+	__KHEAP_MARK;
+
+	if (GetExtended() ) 
+		{
+		test.Printf(_L("t_chunk extended was called. Ready to call TestFullAddressSpace(Etrue) \n"));
+		TestFullAddressSpace(ETrue);	
+		}	
+	else 
+		{
+		test.Start(_L("Testing.."));
+		testAdjustChunk();
+		test.Next(_L("Test1"));
+		test1();
+		test.Next(_L("Test2"));
+		test2(0x80);
+		test.Next(_L("Test3"));
+		test3(0x7000);
+		test.Next(_L("Test4"));
+		test4(0x7000);
+		test.Next(_L("Test5"));
+		test5(0x80);
+		test.Next(_L("Test7"));
+		test7(0x80);
+		test.Next(_L("Test chunk data clearing attributes"));
+		testClear(0x500);
+		testClear(07000);
+		test.Next(_L("Test9"));
+		testShare();
+		test.Next(_L("Test memory notifiers"));
+		testNotifiers();
+		test.Next(_L("FindChunks"));
+		FindChunks();
+		
+		test.Next(_L("Test full address space"));
+		TestFullAddressSpace(EFalse);
+
+#ifdef __WINS__
+		test.Next(_L("Test execution of code in a local code chunk on emulator"));
+		TestExecLocalCode();
+#endif	//  __WINS__
+
+		test.Next(_L("Test for race conditions in chunk closure"));
+		TestClosure();
+		test.End();
+		}	
+
+	test.Close();
+	__KHEAP_MARKEND;
+	
+	
+	return(KErrNone);
+	}