// Copyright (c) 2008-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:
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32def.h>
#include <e32def_private.h>
#include "t_newldd.h"
#include "t_new_classes.h"

RTest test(_L("Testing Operator New"));

TInt RNewLddTest::DoControl(TInt aFunction)
	{
	return RBusLogicalChannel::DoControl(aFunction);
	}

TInt RNewLddTest::Open()
	{
	return DoCreate(KLddName,TVersion(0,1,1),KNullUnit,NULL,NULL);
	}

RNewLddTest lddconn;

TInt TestNew()
	{
	return lddconn.DoControl(RNewLddTest::ENew);
	}
TInt TestPlacementVectorNew()
	{
	return lddconn.DoControl(RNewLddTest::EPlacementVectorNew);
	}
TInt TestVectorNew()
	{
	return lddconn.DoControl(RNewLddTest::EVectorNew);
	}
TInt TestPlacementNew()
	{
	return lddconn.DoControl(RNewLddTest::EPlacementNew);
	}

void UserSideTestNewOOM()
	{
	RDebug::Printf("User-Side: operator new OOM");
	//OOM tests: should not throw
	
	#define TEST_NEW_OOM(CLASS) \
	RDebug::Printf("new " #CLASS);\
		{\
		CLASS* p##CLASS = new CLASS;\
		test_Equal(NULL, p##CLASS);\
		}

	TEST_NEW_OOM(XVeryLargeClassCtorAndDtor);
	TEST_NEW_OOM(XVeryLargeClassCtorOnly);
	TEST_NEW_OOM(XVeryLargeClassDtorOnly);
	TEST_NEW_OOM(XVeryLargeClassNoTors);
	}

void UserSideTestNewConstruction()
	{
	RDebug::Printf("User-Side: operator new non-OOM");
	//Non-OOM:
	

	#define TEST_NEW_CONSTRUCTION(CLASS) \
	RDebug::Printf("new " #CLASS);\
		{\
		CLASS* p##CLASS = new CLASS;\
		test_NotNull(p##CLASS);\
		test_Equal(EConstructed, (p##CLASS)->iState);\
		delete p##CLASS;\
		}

	TEST_NEW_CONSTRUCTION(XCtorAndDtor)
	TEST_NEW_CONSTRUCTION(XCtorOnly)


	#define TEST_NEW(CLASS) \
	RDebug::Printf("new " #CLASS);\
		{\
		CLASS* p##CLASS = new CLASS;\
		test_NotNull(p##CLASS);\
		delete p##CLASS;\
		}
	TEST_NEW(XDtorOnly)
	TEST_NEW(XNoTors)
	}
	
void UserSideTestNew()
	{
	UserSideTestNewOOM();
	UserSideTestNewConstruction();
	}

void TrappedUserSideTestNew()
	{
	RDebug::Printf("TRAPPED User-Side: operator new");
	//OOM tests: should not throw

	#define TEST_NEW_ELEAVE_OOM(CLASS) \
	RDebug::Printf("new(ELeave) " #CLASS);\
		{\
		TRAPD(r, new(ELeave) (CLASS));\
		test_Equal(KErrNoMemory, r);\
		}

	TEST_NEW_ELEAVE_OOM(XVeryLargeClassCtorAndDtor);
	TEST_NEW_ELEAVE_OOM(XVeryLargeClassCtorOnly);
	TEST_NEW_ELEAVE_OOM(XVeryLargeClassDtorOnly);
	TEST_NEW_ELEAVE_OOM(XVeryLargeClassNoTors);



	RDebug::Printf("User-Side: operator new non-OOM");
	//Non-OOM:

	#define TEST_NEW_ELEAVE(CLASS, TEST_CTOR ) \
	RDebug::Printf("new(ELeave) " #CLASS);\
		{\
		CLASS* p##CLASS=NULL;\
		TRAPD(r, p##CLASS = new(ELeave) (CLASS));\
		test_KErrNone(r);\
		volatile TBool testCtor=(TEST_CTOR);\
		if(testCtor)\
			{\
			test_Equal(EConstructed, (p##CLASS)->iState);\
			}\
		delete p##CLASS;\
		}

	TEST_NEW_ELEAVE(XCtorAndDtor, ETrue);
	TEST_NEW_ELEAVE(XCtorOnly, ETrue);
	TEST_NEW_ELEAVE(XDtorOnly, EFalse);
	TEST_NEW_ELEAVE(XNoTors, EFalse);
	}


#define TEST_ARRAY_CONSTRUCTION(ARRAY, LENGTH)\
	{\
	for(TInt i=0; i<(LENGTH); ++i)\
		{\
		test_Equal(EConstructed, ARRAY[i].iState);\
		}\
	}

void UserSideTestVectorNew()
	{
	RDebug::Printf("User-Side:vector operator new");
	RDebug::Printf("OOM tests");

#if defined(__ARMCC__) && __ARMCC_VERSION >= 400000 // Work-around a problem where compsupp doesn't overload the rvct 4.0 runtime 
	#define TEST_VEC_NEW_OOM(CLASS) \
	RDebug::Printf("new " #CLASS "[%d]", KOOMArraySize );\
		{\
		CLASS* p##CLASS = new(std::nothrow) CLASS[KOOMArraySize];\
		test_Equal(NULL, p##CLASS);\
		}
#else
	#define TEST_VEC_NEW_OOM(CLASS) \
	RDebug::Printf("new " #CLASS "[%d]", KOOMArraySize );\
		{\
		CLASS* p##CLASS = new CLASS[KOOMArraySize];\
		test_Equal(NULL, p##CLASS);\
		}
#endif

	TEST_VEC_NEW_OOM(XCtorAndDtor);
	TEST_VEC_NEW_OOM(XCtorOnly);
	TEST_VEC_NEW_OOM(XDtorOnly);
	TEST_VEC_NEW_OOM(XNoTors);
	
	RDebug::Printf("non-OOM tests");

	#define TEST_VEC_NEW(CLASS, ARRAY_LENGTH, TEST_CTOR) \
	RDebug::Printf("new " #CLASS "[%d]", ARRAY_LENGTH);\
		{\
		CLASS* p##CLASS = new CLASS[(ARRAY_LENGTH)];\
		test_NotNull(p##CLASS);\
		volatile TBool testCtor=(TEST_CTOR);\
		if(testCtor)\
			{\
			TEST_ARRAY_CONSTRUCTION(p##CLASS, ARRAY_LENGTH);\
			}\
		delete[] p##CLASS;\
		}

	TEST_VEC_NEW(XCtorAndDtor, KTestArrayLength, ETrue);
	TEST_VEC_NEW(XCtorOnly, KTestArrayLength, ETrue);
	TEST_VEC_NEW(XDtorOnly, KTestArrayLength, EFalse);
	TEST_VEC_NEW(XNoTors, KTestArrayLength, EFalse);
	}

void TrappedUserSideTestVectorNew()
	{
	RDebug::Printf("User-Side:vector operator new");
	RDebug::Printf("OOM tests");

	#define TEST_VEC_NEW_ELEAVE_OOM(CLASS) \
	RDebug::Printf("new(ELeave) " #CLASS "[%d]", KOOMArraySize );\
		{\
	   	TRAPD(r, new(ELeave) CLASS[KOOMArraySize];)\
		test_Equal(KErrNoMemory, r);\
		}

	TEST_VEC_NEW_ELEAVE_OOM(XCtorAndDtor);
	TEST_VEC_NEW_ELEAVE_OOM(XCtorOnly);
	TEST_VEC_NEW_ELEAVE_OOM(XDtorOnly);
	TEST_VEC_NEW_ELEAVE_OOM(XNoTors);



	RDebug::Printf("non-OOM tests");
	#define TEST_VEC_NEW_ELEAVE(CLASS, ARRAY_LENGTH, TEST_CTOR) \
	RDebug::Printf("new(ELeave) " #CLASS "[%d]", ARRAY_LENGTH);\
		{\
		CLASS* p##CLASS = NULL;\
		TRAPD(r, p##CLASS = new(ELeave) CLASS[(ARRAY_LENGTH)]);\
		test_KErrNone(r);\
		TBool testCtor=(TEST_CTOR);\
		if(testCtor)\
			{\
			TEST_ARRAY_CONSTRUCTION(p##CLASS, ARRAY_LENGTH);\
			}\
		delete[] p##CLASS;\
		}

	TEST_VEC_NEW_ELEAVE(XCtorAndDtor, KTestArrayLength, ETrue);
	TEST_VEC_NEW_ELEAVE(XCtorOnly, KTestArrayLength, ETrue);
	TEST_VEC_NEW_ELEAVE(XDtorOnly, KTestArrayLength, EFalse);
	TEST_VEC_NEW_ELEAVE(XNoTors, KTestArrayLength, EFalse);
	}

void UserSideTestPlacementNew()
	{
	RDebug::Printf("::UserSideTestPlacementNew");
	
	#define TEST_PLACMENT_NEW(CLASS, POST_CTOR_STATE, POST_DTOR_STATE)\
		{\
		void* someram = User::AllocZ(sizeof(CLASS));\
		test_NotNull(someram);\
		RDebug::Printf("new (someram) " #CLASS);\
		CLASS* p##CLASS = new (someram) CLASS;\
		test_Equal(someram, p##CLASS);\
		test_Equal(POST_CTOR_STATE, p##CLASS->iState);\
		p##CLASS->~CLASS();\
		test_Equal(POST_DTOR_STATE, p##CLASS->iState);\
		User::Free(someram);\
		p##CLASS=NULL;\
		}\

	TEST_PLACMENT_NEW(XCtorAndDtor, EConstructed, EDeconstructed);
	TEST_PLACMENT_NEW(XCtorOnly, EConstructed, EConstructed);
	TEST_PLACMENT_NEW(XDtorOnly, ENull, EDeconstructed);
	TEST_PLACMENT_NEW(XNoTors, ENull, ENull);
	}


void UserSideTestPlacementVectorNew()
	{
	__UHEAP_MARK;

	RDebug::Printf("::UserSideTestPlacementVectorNew");

	#define TEST_VEC_PLACEMENT_NEW(CLASS, ARRAY_LENGTH, POST_CTOR_STATE, POST_DTOR_STATE)\
	RDebug::Printf("new(someram) " #CLASS "[%d]", ARRAY_LENGTH);\
		{\
		void* someram = User::AllocZ(sizeof(CLASS) * ARRAY_LENGTH);\
		test_NotNull(someram);\
		CLASS* p##CLASS = new (someram) CLASS[(ARRAY_LENGTH)];\
		for(TInt i=0; i<(ARRAY_LENGTH); ++i)\
			{\
			test_Equal(POST_CTOR_STATE, p##CLASS[i].iState);\
			p##CLASS[i].~CLASS();\
			test_Equal(POST_DTOR_STATE, p##CLASS[i].iState);\
			}\
		User::Free(someram);\
		}

	TEST_VEC_PLACEMENT_NEW(XCtorAndDtor, KTestArrayLength, EConstructed, EDeconstructed);
	TEST_VEC_PLACEMENT_NEW(XCtorOnly, KTestArrayLength, EConstructed, EConstructed);
	TEST_VEC_PLACEMENT_NEW(XDtorOnly, KTestArrayLength, ENull, EDeconstructed);
	TEST_VEC_PLACEMENT_NEW(XNoTors, KTestArrayLength, ENull, ENull);

	__UHEAP_MARKEND;
	}


TInt E32Main()
	{
	__UHEAP_MARK;

	test.Start(_L("Testing operator new"));
	
	test.Next(_L("Installing LDD"));
	TInt r=User::LoadLogicalDevice(KKInstallLddName);
	test(r==KErrNone || r==KErrAlreadyExists);
	
	__KHEAP_MARK;

	#define TEST_THIS(X) test.Next(_L(#X)); (X)
	
	TEST_THIS(UserSideTestNew());
	TEST_THIS(UserSideTestPlacementNew());

// Workaround for bug in MSVC6/CW compilers. The following test case fails on
// WINS, WINSCW and X86 targets (not X86GCC), where the placement vector new
// operator does not behave as expected, ultimately resulting in heap corruption
// when the parameter to placement array new is passed on the stack.
#if !(defined(__WINS__) || (defined(__VC32__) && (_MSC_VER < 1300)))
	TEST_THIS(UserSideTestPlacementVectorNew());
#else
	test.Next(_L("Emulator and/or VC32 - Skipped: UserSideTestPlacementVectorNew"));
#endif

	TEST_THIS(UserSideTestVectorNew());
	TEST_THIS(TrappedUserSideTestNew());
	TEST_THIS(TrappedUserSideTestVectorNew());
	
	r=lddconn.Open();
	test_KErrNone(r);

	test.Next(_L("Kernel-side:Normal operator new"));
	r = TestNew();
	test_KErrNone(r);
		
	test.Next(_L("Kernel-side:Placement operator new"));
	r = TestPlacementNew();
	test_KErrNone(r);

	test.Next(_L("Kernel-side:Placement Vector operator new"));
	r = TestPlacementVectorNew();
	test_KErrNone(r);

	test.Next(_L("Kernel-side:Vector operator new"));
	r = TestVectorNew();
	test_KErrNone(r);

	r = RTest::CloseHandleAndWaitForDestruction(lddconn);
	test_KErrNone(r);

	test.End();
	test.Close();

	__KHEAP_MARKEND;
	__UHEAP_MARKEND;
	return KErrNone;
	}

