/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////


#include <EABase/eabase.h>
#include "EASTLTest.h"
#include "GetTypeName.h"
#include <EAStdC/EAString.h>
#include <EAStdC/EAStopwatch.h>
#include <EASTL/atomic.h>
#include <EASTL/core_allocator_adapter.h>
#include <EASTL/core_allocator.h>
#include <EASTL/intrusive_ptr.h>
#include <EASTL/linked_array.h>
#include <EASTL/linked_ptr.h>
#include <EASTL/safe_ptr.h>
#include <EASTL/scoped_array.h>
#include <EASTL/scoped_ptr.h>
#include <EASTL/shared_array.h>
#include <EASTL/shared_ptr.h>
#include <EASTL/unique_ptr.h>
#include <EASTL/weak_ptr.h>
#include <eathread/eathread_thread.h>

EA_DISABLE_ALL_VC_WARNINGS()
#include <stdio.h>
#include <string.h>
#ifdef EA_PLATFORM_WINDOWS
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN
	#endif
	#include <Windows.h>
#elif defined(EA_PLATFORM_ANDROID)
	#include <android/log.h>
#endif
EA_RESTORE_ALL_VC_WARNINGS()

EA_DISABLE_VC_WARNING(4702 4800)  // 4702: unreachable code
								  // 4800: forcing value to bool 'true' or 'false'



namespace SmartPtrTest
{
	/// CustomDeleter
	///
	/// Used for testing unique_ptr deleter overrides. Otherwise acts the same as the default deleter.
	///
	struct CustomDeleter
	{
		template <typename T>
		void operator()(const T* p) const // We use a const argument type in order to be most flexible with what types we accept. 
			{ delete const_cast<T*>(p); }

		CustomDeleter() {}
		CustomDeleter(const CustomDeleter&) {}
		CustomDeleter(CustomDeleter&&) {}
		CustomDeleter& operator=(const CustomDeleter&) { return *this; }
		CustomDeleter& operator=(CustomDeleter&&) { return *this; }
	};


	struct CustomArrayDeleter
	{
		template <typename T>
		void operator()(const T* p) const // We use a const argument type in order to be most flexible with what types we accept. 
			{ delete[] const_cast<T*>(p); }

		CustomArrayDeleter() {}
		CustomArrayDeleter(const CustomArrayDeleter&) {}
		CustomArrayDeleter(CustomArrayDeleter&&) {}
		CustomArrayDeleter& operator=(const CustomArrayDeleter&) { return *this; }
		CustomArrayDeleter& operator=(CustomArrayDeleter&&) { return *this; }
	};


	/// A
	///
	/// This is used for various tests. 
	///
	struct A
	{
		char mc;
		static int mCount;

		A(char c = 0) 
			: mc(c) { ++mCount; }

		A(const A& x) 
			: mc(x.mc) { ++mCount; }

		A& operator=(const A& x) 
			{ mc = x.mc; return *this; }

		virtual ~A() // Virtual because we subclass A below.
			{ --mCount; }
	};


	int A::mCount = 0;


	/// B
	///
	struct B : public A
	{
	};



	/// RefCountTest
	///
	/// This is used for tests involving intrusive_ptr.
	/// 
	struct RefCountTest
	{
		int mRefCount;
		static int mCount;

		RefCountTest()
			: mRefCount(0) { ++mCount; }

		RefCountTest(const RefCountTest&)
			: mRefCount(0) { ++mCount; }

		RefCountTest& operator=(const RefCountTest&)
			{ return *this; }

		virtual ~RefCountTest()
			{ --mCount; }

		virtual int AddRef()
			{ return (int)((mRefCount++) + 1); }

		virtual int Release()
		{
			int rc = (int)((mRefCount--) - 1);
			if(rc)
				return rc;
			mRefCount = 1;
			delete this;
			return 0;
		}
	};

	int RefCountTest::mCount = 0;



	/// Test
	///
	/// This is used for tests involving intrusive_ptr.
	/// 
	struct Test : public RefCountTest
	{
		bool* mpBool;

		Test(bool* pBool)
			: mpBool(pBool) { *pBool = true; }

		Test(const Test& x): 
			RefCountTest(x), mpBool(x.mpBool) { }

		Test& operator=(const Test& x)
			{ mpBool = x.mpBool; return *this; }

	   ~Test()
			{ *mpBool = false; }
	};



	/// IntrusiveParent / IntrusiveChild
	///
	/// This is used for tests involving intrusive_ptr.
	/// 
	struct IntrusiveParent : public RefCountTest
	{
	};

	struct IntrusiveChild : public IntrusiveParent
	{
	};


	/// intrusive_ptr_add_ref / intrusive_ptr_release
	///
	/// This is used for tests involving intrusive_ptr.
	/// 
	struct IntrusiveCustom : public RefCountTest
	{
		static int mAddRefCallCount;
		static int mReleaseCallCount;

		virtual int AddRef()
		{
			++mAddRefCallCount;
			return RefCountTest::AddRef();
		}

		virtual int Release()
		{
			++mReleaseCallCount;
			return RefCountTest::Release();
		}
	};

	int IntrusiveCustom::mAddRefCallCount  = 0;
	int IntrusiveCustom::mReleaseCallCount = 0;

	void intrusive_ptr_add_ref(IntrusiveCustom* p)
	{
		p->AddRef();
	}
	 
	void intrusive_ptr_release(IntrusiveCustom* p)
	{
		p->Release();
	}


	/// ParentClass / ChildClass / GrandChildClass
	///
	/// This is used for tests involving shared_ptr.
	/// 
	struct ParentClass
	{
		virtual ~ParentClass() { }
		virtual void DoNothingParentClass() { }
	};

	struct ChildClass : public ParentClass
	{
		virtual void DoNothingChildClass() { }
	};

	struct GrandChildClass : public ChildClass
	{
		virtual void DoNothingGrandChildClass() { }
	};



	/// NamedClass
	///
	struct NamedClass
	{
		const char* mpName;
		const char* mpName2;
		static int  mnCount;

		NamedClass(const char* pName = NULL)
			: mpName(pName), mpName2(NULL) { ++mnCount; }

		NamedClass(const char* pName, const char* pName2)
			: mpName(pName), mpName2(pName2) { ++mnCount; }

		NamedClass(const NamedClass& x)
			: mpName(x.mpName), mpName2(x.mpName2) { ++mnCount; }

		NamedClass& operator=(const NamedClass& x)
			{ mpName = x.mpName; mpName2 = x.mpName2; return *this; }

	   ~NamedClass()
			{ --mnCount; }
	};

	int NamedClass::mnCount = 0;



	/// Y
	///
	/// This is used for tests involving shared_ptr and enabled_shared_from_this.
	/// 
	struct Y : public eastl::enable_shared_from_this<Y>
	{
		static int mnCount;

		Y() { ++mnCount; }
		Y(const Y&) { ++mnCount; }
		Y& operator=(const Y&) { return *this; }
	   ~Y() { --mnCount; }

		eastl::shared_ptr<Y> f()
			{ return shared_from_this(); }
	};

	int Y::mnCount = 0;



	/// ACLS / BCLS
	///
	/// This is used for tests involving shared_ptr.
	/// 
	class ACLS : public eastl::enable_shared_from_this<ACLS>
	{
	public:
		static int mnCount;
		int a;

		ACLS(int _a_ = 0) : a(_a_) { ++mnCount; }
		ACLS(const ACLS& x) : a(x.a) { ++mnCount; }
		ACLS& operator=(const ACLS& x) { a = x.a; return *this; }
	   ~ACLS() { --mnCount; }
	};

	int ACLS::mnCount = 0;


	class BCLS : public ACLS
	{
	public:
		static int mnCount;
		int b;

		BCLS(int _b_ = 0) : b(_b_) { ++mnCount; }
		BCLS(const BCLS& x) : ACLS(x), b(x.b) { ++mnCount; }
		BCLS& operator=(const BCLS& x) { b = x.b; ACLS::operator=(x); return *this; }
	   ~BCLS() { --mnCount; }
	};

	int BCLS::mnCount = 0;



	/// A1 / B1
	///
	/// This is used for tests involving shared_ptr.
	/// 
	struct A1
	{
		static int mnCount;
		int a;

		A1(int _a_ = 0) : a(_a_) { ++mnCount; }
		A1(const A1& x) : a(x.a) { ++mnCount; }
		A1& operator=(const A1& x) { a = x.a; return *this; }
	   ~A1() { --mnCount; }
	};

	int A1::mnCount = 0;



	struct B1 : public A1
	{
		static int mnCount;
		int b;

		B1(int _b_ = 0) : b(_b_) { ++mnCount; }
		B1(const B1& x) : A1(x), b(x.b) { ++mnCount; }
		B1& operator=(const B1& x) { b = x.b; A1::operator=(x); return *this; }
	   ~B1() { --mnCount; }
	};

	int B1::mnCount = 0;



	class MockObject
	{
	public:
		MockObject(bool* pAlloc) 
		  : mpAlloc(pAlloc){ *mpAlloc = true; }

	   ~MockObject()
			{ *mpAlloc = false; }

		bool IsAllocated() const
			{ return *mpAlloc; }

		bool* GetAllocPtr() const
			{ return mpAlloc; }

	private:
		bool* mpAlloc;
	};

	class DerivedMockObject : public MockObject
	{
	public:
		DerivedMockObject(bool* pAlloc)
		  : MockObject(pAlloc) {}
	};


	struct foo : public eastl::enable_shared_from_this<foo>
	{
		foo() : mX(0){}
		int mX;
	};

	struct CheckUPtrEmptyInDestructor
	{
		~CheckUPtrEmptyInDestructor()
		{
			if(mpUPtr)
				mCheckUPtrEmpty = (*mpUPtr == nullptr);
		}

		eastl::unique_ptr<CheckUPtrEmptyInDestructor>* mpUPtr{};
		static bool mCheckUPtrEmpty;
	};

	bool CheckUPtrEmptyInDestructor::mCheckUPtrEmpty = false;

	struct CheckUPtrArrayEmptyInDestructor
	{
		~CheckUPtrArrayEmptyInDestructor()
		{
			if(mpUPtr)
				mCheckUPtrEmpty = (*mpUPtr == nullptr);
		}

		eastl::unique_ptr<CheckUPtrArrayEmptyInDestructor[]>* mpUPtr{};
		static bool mCheckUPtrEmpty;
	};

	bool CheckUPtrArrayEmptyInDestructor::mCheckUPtrEmpty = false;
} // namespace SmartPtrTest




static int Test_unique_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		EATEST_VERIFY(A::mCount == 0);

		// explicit unique_ptr(pointer pValue) noexcept
		unique_ptr<int> pT1(new int(5));
		EATEST_VERIFY(*pT1 == 5);

		// (reference) operator*() const
		*pT1 = 3;
		EATEST_VERIFY(*pT1 == 3);

		// explicit unique_ptr(pointer pValue) noexcept
		unique_ptr<A> pT2(new A(1));
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(A::mCount == 1);

		// Pointers of derived types are allowed (unlike array unique_ptr) 
		unique_ptr<A> pT1B(new B);
		EATEST_VERIFY(pT1B.get() != NULL);
		EATEST_VERIFY(A::mCount == 2);

		A* pA = pT1B.release(); // release simply forgets the owned pointer.
		EATEST_VERIFY(pT1B.get() == NULL);
		EATEST_VERIFY(A::mCount == 2);

		delete pA;
		EATEST_VERIFY(A::mCount == 1);

		// pointer operator->() const noexcept
		pT2->mc = 5;
		EATEST_VERIFY(pT2.get()->mc == 5);

		// void reset(pointer pValue = pointer()) noexcept
		pT2.reset(new A(2));
		EATEST_VERIFY(pT2->mc == 2);
		EATEST_VERIFY(A::mCount == 1);

		pT2.reset(0);
		EATEST_VERIFY(pT2.get() == (A*)0);
		EATEST_VERIFY(A::mCount == 0);

		pT2.reset(new A(3));
		EATEST_VERIFY(pT2->mc == 3);
		EATEST_VERIFY(A::mCount == 1);

		unique_ptr<A> pT3(new A(4));
		EATEST_VERIFY(pT3->mc == 4);
		EATEST_VERIFY(A::mCount == 2);

		// void swap(this_type& scopedPtr) noexcept
		pT2.swap(pT3);
		EATEST_VERIFY(pT2->mc == 4);
		EATEST_VERIFY(pT3->mc == 3);
		EATEST_VERIFY(A::mCount == 2);

		// void swap(unique_ptr<T, D>& scopedPtr1, unique_ptr<T, D>& scopedPtr2) noexcept
		swap(pT2, pT3);
		EATEST_VERIFY(pT2->mc == 3);
		EATEST_VERIFY(pT3->mc == 4);
		EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get()));
		EATEST_VERIFY(A::mCount == 2);

		// pointer release() noexcept
		unique_ptr<A> pRelease(new A);
		EATEST_VERIFY(A::mCount == 3);
		pA = pRelease.release();
		delete pA;
		EATEST_VERIFY(A::mCount == 2);

		// constexpr unique_ptr() noexcept
		unique_ptr<A> pT4;
		EATEST_VERIFY(pT4.get() == (A*)0);
		if(pT4)
			EATEST_VERIFY(pT4.get()); // Will fail
		if(!(!pT4))
			EATEST_VERIFY(pT4.get()); // Will fail

		pT4.reset(new A(0));
		if(!pT4)
			EATEST_VERIFY(!pT4.get()); // Will fail

		EATEST_VERIFY(A::mCount == 3);

		// unique_ptr(nullptr_t) noexcept
		unique_ptr<A> pT5(nullptr);
		EATEST_VERIFY(pT5.get() == (A*)0);

		// unique_ptr(pointer pValue, deleter) noexcept
		CustomDeleter customADeleter;
		unique_ptr<A, CustomDeleter> pT6(new A(17), customADeleter);
		EATEST_VERIFY(pT6->mc == 17);

		// unique_ptr(pointer pValue, typename eastl::remove_reference<Deleter>::type&& deleter) noexcept
		unique_ptr<A, CustomDeleter> pT7(new A(18), CustomDeleter());
		EATEST_VERIFY(pT7->mc == 18);

		// unique_ptr(this_type&& x) noexcept
		unique_ptr<A, CustomDeleter> pT8(eastl::move(pT7));
		EATEST_VERIFY(pT8->mc == 18);

		// unique_ptr(unique_ptr<U, E>&& u, ...)
		unique_ptr<A, default_delete<A> > pT9(eastl::move(pT2));

		// this_type& operator=(this_type&& u) noexcept
		// operator=(unique_ptr<U, E>&& u) noexcept
		//unique_ptr<void, CustomDeleter> pTVoid;
		//unique_ptr<int, CustomDeleter>  pTInt(new int(1));
		//pTVoid.operator=<int, CustomDeleter>(eastl::move(pTInt));  // This doesn't work because CustomDeleter doesn't know how to delete void*. Need to rework this test.

		// this_type& operator=(nullptr_t) noexcept
		pT6 = nullptr;
		EATEST_VERIFY(pT6.get() == (A*)0);

		// user reported regression
		// ensure a unique_ptr containing nullptr doesn't call the deleter when its destroyed.
		{
			static bool sLocalDeleterCalled;
			sLocalDeleterCalled = false;

			struct LocalDeleter
			{
				void operator()(int* p) const
				{
					sLocalDeleterCalled = true;
					delete p;
				}
			};

			using local_unique_ptr = eastl::unique_ptr<int, LocalDeleter>;

			local_unique_ptr pEmpty{nullptr};

			pEmpty = local_unique_ptr{new int(42), LocalDeleter()};

			EATEST_VERIFY(sLocalDeleterCalled == false);
		}
	}

	{
		// Test that unique_ptr internal pointer is reset before calling the destructor
		CheckUPtrEmptyInDestructor::mCheckUPtrEmpty = false;

		unique_ptr<CheckUPtrEmptyInDestructor> uptr(new CheckUPtrEmptyInDestructor);
		uptr->mpUPtr = &uptr;
		uptr.reset();
		EATEST_VERIFY(CheckUPtrEmptyInDestructor::mCheckUPtrEmpty);
	}

	{
		// Test that unique_ptr<[]> internal pointer is reset before calling the destructor
		CheckUPtrArrayEmptyInDestructor::mCheckUPtrEmpty = false;

		unique_ptr<CheckUPtrArrayEmptyInDestructor[]> uptr(new CheckUPtrArrayEmptyInDestructor[1]);
		uptr[0].mpUPtr = &uptr;
		uptr.reset();
		EATEST_VERIFY(CheckUPtrArrayEmptyInDestructor::mCheckUPtrEmpty);
	}

	{
		#if EASTL_CORE_ALLOCATOR_ENABLED
			// Test EA::Allocator::EASTLICoreDeleter usage within eastl::shared_ptr.
			// http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr

			// Consider the following for standards compliance.
			// eastl::shared_ptr<A, EASTLCoreDeleterAdapter> foo(pA, EASTLCoreDeleterAdapter());

			const int cacheAllocationCount = gEASTLTest_AllocationCount;

			using namespace EA::Allocator;

			EASTLCoreAllocatorAdapter ta;
			void* pMem = ta.allocate(sizeof(A));

			EATEST_VERIFY(pMem != nullptr);     
			EATEST_VERIFY(gEASTLTest_AllocationCount > cacheAllocationCount);
			{            
				A* pA = new (pMem) A();
				eastl::shared_ptr<A> foo(pA, EASTLCoreDeleterAdapter());  // Not standards complaint code.  Update EASTL implementation to provide the type of the deleter.
			}
			EATEST_VERIFY(gEASTLTest_AllocationCount == cacheAllocationCount);
			EATEST_VERIFY(A::mCount == 0);
		#endif
	}

	{
		// Test array specialization of unique_ptr

		EATEST_VERIFY(A::mCount == 0);

		// template <typename P>
		// explicit unique_ptr(P pValue) noexcept
		unique_ptr<int[]> pT1(new int[5]);
		pT1[0] = 5;
		EATEST_VERIFY(pT1[0] == 5);

		// Arrays of derived types are not allowed (unlike regular unique_ptr) 
		// unique_ptr<A[]> pT1B(new B[5]); // Disabled because it should not compile.

		// (reference) operator[]() const
		pT1[1] = 1;
		EATEST_VERIFY(pT1[1] == 1);

		// explicit unique_ptr(pointer pValue) noexcept
		unique_ptr<A[]> pT2(new A[1]);
		pT2[0].mc = 1;
		EATEST_VERIFY(pT2[0].mc == 1);
		EATEST_VERIFY(A::mCount == 1);

		// pointer operator->() const noexcept
		pT2[0].mc = 5;
		EATEST_VERIFY(pT2[0].mc == 5);

		// void reset(pointer pValue = pointer()) noexcept
		pT2.reset(new A[2]);
		pT2[0].mc = 2;
		EATEST_VERIFY(pT2[0].mc == 2);

		pT2.reset(0);
		EATEST_VERIFY(pT2.get() == (A*)0);

		pT2.reset(new A[3]);
		pT2[0].mc = 3;
		EATEST_VERIFY(pT2[0].mc == 3);

		unique_ptr<A[]> pT3(new A[4]);
		pT3[0].mc = 4;
		EATEST_VERIFY(pT3[0].mc == 4);

		// void swap(this_type& scopedPtr) noexcept
		pT2.swap(pT3);
		EATEST_VERIFY(pT2[0].mc == 4);
		EATEST_VERIFY(pT3[0].mc == 3);

		// void swap(unique_ptr<T, D>& scopedPtr1, unique_ptr<T, D>& scopedPtr2) noexcept
		swap(pT2, pT3);
		EATEST_VERIFY(pT2[0].mc == 3);
		EATEST_VERIFY(pT3[0].mc == 4);
		EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get()));

		// pointer release() noexcept
		unique_ptr<A[]> pRelease(new A[1]);
		A* pAArray = pRelease.release();
		delete[] pAArray;

		// constexpr unique_ptr() noexcept
		unique_ptr<A[]> pT4;
		EATEST_VERIFY(pT4.get() == (A*)0);
		if(pT4)
			EATEST_VERIFY(pT4.get()); // Will fail
		if(!(!pT4))
			EATEST_VERIFY(pT4.get()); // Will fail

		pT4.reset(new A[1]);
		if(!pT4)
			EATEST_VERIFY(!pT4.get()); // Will fail

		EATEST_VERIFY(A::mCount == 8);  // There were a number of array creations and deletions above that make this so.

		// unique_ptr(nullptr_t) noexcept
		unique_ptr<A[]> pT5(nullptr);
		EATEST_VERIFY(pT5.get() == (A*)0);

		// unique_ptr(pointer pValue, deleter) noexcept
		CustomArrayDeleter customADeleter;
		unique_ptr<A[], CustomArrayDeleter> pT6(new A[17], customADeleter);
		pT6[0].mc = 17;
		EATEST_VERIFY(pT6[0].mc == 17);

		// unique_ptr(pointer pValue, typename eastl::remove_reference<Deleter>::type&& deleter) noexcept
		unique_ptr<A[], CustomArrayDeleter> pT7(new A[18], CustomArrayDeleter());
		pT7[0].mc = 18;
		EATEST_VERIFY(pT7[0].mc == 18);

		// unique_ptr(this_type&& x) noexcept
		unique_ptr<A[], CustomArrayDeleter> pT8(eastl::move(pT7));
		EATEST_VERIFY(pT8[0].mc == 18);

		// unique_ptr(unique_ptr<U, E>&& u, ...)
		unique_ptr<A[], default_delete<A[]> > pT9(eastl::move(pT2));
		EATEST_VERIFY(pT9[0].mc == 3);

		// this_type& operator=(this_type&& u) noexcept
		// operator=(unique_ptr<U, E>&& u) noexcept
		//unique_ptr<void, CustomDeleter> pTVoid;
		//unique_ptr<int, CustomDeleter>  pTInt(new int(1));
		//pTVoid.operator=<int, CustomDeleter>(eastl::move(pTInt));  // This doesn't work because CustomDeleter doesn't know how to delete void*. Need to rework this test.

		// this_type& operator=(nullptr_t) noexcept
		pT6 = nullptr;
		EATEST_VERIFY(pT6.get() == (A*)0);

		// unique_ptr<> make_unique(Args&&... args);
		unique_ptr<NamedClass> p = eastl::make_unique<NamedClass>("test", "test2");
		EATEST_VERIFY(EA::StdC::Strcmp(p->mpName, "test") == 0 && EA::StdC::Strcmp(p->mpName2, "test2") == 0);

		unique_ptr<NamedClass[]> pArray = eastl::make_unique<NamedClass[]>(4);
		pArray[0].mpName = "test";
		EATEST_VERIFY(EA::StdC::Strcmp(p->mpName, "test") == 0);

		#ifdef EASTL_TEST_DISABLED_PENDING_SUPPORT
		{
			const size_t kAlignedStructAlignment = 512;
			struct AlignedStruct {} EA_ALIGN(kAlignedStructAlignment);

			unique_ptr<AlignedStruct> pAlignedStruct = eastl::make_unique<AlignedStruct>();
			EATEST_VERIFY_F(intptr_t(pAlignedStruct.get()) % kAlignedStructAlignment == 0, "pAlignedStruct didn't have proper alignment");
		}
		#endif

		//Expected to not be valid:
		//unique_ptr<NamedClass[4]> p2Array4 = eastl::make_unique<NamedClass[4]>();
		//p2Array4[0].mpName = "test";
		//EATEST_VERIFY(EA::StdC::Strcmp(p2Array4[0].mpName, "test") == 0);
	}

	EATEST_VERIFY(A::mCount == 0); // This check verifies that no A instances were lost, which also verifies that the [] version of the deleter was used in all cases.

	// validate unique_ptr's compressed_pair implementation is working. 
	{
		const int ARBITRARY_SIZE = 256;
		static_assert(sizeof(unique_ptr<short>)     == sizeof(uintptr_t), "");
		static_assert(sizeof(unique_ptr<long>)      == sizeof(uintptr_t), "");

		// unique_ptr should be the same size as a pointer. The deleter object is empty so the
		// eastl::compressed_pair implementation will remove that deleter data member from the unique_ptr.
		{
			auto deleter = [](void* pMem) { free(pMem); };
			unique_ptr<void, decltype(deleter)> sptr(malloc(ARBITRARY_SIZE), deleter);
			static_assert(sizeof(sptr) == (sizeof(uintptr_t)), "unexpected unique_ptr size");
		}

		// unique_ptr should be larger than a pointer when the deleter functor is capturing state.  This state forces
		// the compressed_pair to cached the data in unique_ptr locally. 
		{
			int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0;
			auto deleter = [=](void* pMem) { auto result = (a+b+c+d+e+f); EA_UNUSED(result); free(pMem); };
			unique_ptr<void, decltype(deleter)> sptr(malloc(ARBITRARY_SIZE), deleter);
			static_assert(sizeof(sptr) == ((6 * sizeof(int)) + (sizeof(uintptr_t))), "unexpected unique_ptr size");
		}

		// Simply test moving the one unique pointer to another.
		// Exercising operator=(T&&)
		{
			{
				unique_ptr<int> ptr(new int(3));
				EATEST_VERIFY(ptr.get() && *ptr == 3);

				unique_ptr<int> newPtr(new int(4));
				EATEST_VERIFY(newPtr.get() && *newPtr == 4);

				ptr = eastl::move(newPtr);  // Deletes int(3) and assigns mpValue to int(4)
				EATEST_VERIFY(ptr.get() && *ptr == 4);
				EATEST_VERIFY(newPtr.get() == nullptr);
			}

		#if EA_HAVE_CPP11_INITIALIZER_LIST
			{
				unique_ptr<int[]> ptr(new int[3]{ 0, 1, 2 });
				EATEST_VERIFY(ptr.get() && ptr[0] == 0 && ptr[1] == 1 && ptr[2] == 2);

				unique_ptr<int[]> newPtr(new int[3]{ 3, 4, 5 });
				EATEST_VERIFY(newPtr.get() && newPtr[0] == 3 && newPtr[1] == 4 && newPtr[2] == 5);

				ptr = eastl::move(newPtr);  // Deletes int(3) and assigns mpValue to int(4)
				EATEST_VERIFY(ptr.get() && ptr[0] == 3 && ptr[1] == 4 && ptr[2] == 5);
				EATEST_VERIFY(newPtr.get() == nullptr);
			}
		#endif

		#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
			{
				unique_ptr<int> pT1(new int(5));
				unique_ptr<int> pT2(new int(10));
				unique_ptr<int> pT3(new int(0));

				EATEST_VERIFY((pT1 <=> pT2) != 0);
				EATEST_VERIFY((pT2 <=> pT1) != 0);

				EATEST_VERIFY((pT1 <=> pT2) < 0);
				EATEST_VERIFY((pT1 <=> pT2) <= 0);
				EATEST_VERIFY((pT2 <=> pT1) > 0);
				EATEST_VERIFY((pT2 <=> pT1) >= 0);

				EATEST_VERIFY((pT3 <=> pT1) < 0);
				EATEST_VERIFY((pT3 <=> pT2) < 0);
				EATEST_VERIFY((pT1 <=> pT3) > 0);
				EATEST_VERIFY((pT2 <=> pT3) > 0);

				unique_ptr<A> pT4(new A(5));
				unique_ptr<A> pT5(new A(10));

				EATEST_VERIFY((pT4 <=> pT5) != 0);
				EATEST_VERIFY((pT5 <=> pT4) != 0);

				EATEST_VERIFY((pT4 <=> pT5) < 0);
				EATEST_VERIFY((pT4 <=> pT5) <= 0);
				EATEST_VERIFY((pT5 <=> pT4) > 0);
				EATEST_VERIFY((pT5 <=> pT4) >= 0);
			}
		#endif

			// ToDo: Test move assignment between two convertible types with an is_assignable deleter_type
			//{
			//	struct Base {};
			//	struct Child : public Base {};

			//	typedef unique_ptr<Base, CustomDeleter> BaseSPtr;
			//	typedef unique_ptr<Child, CustomDeleter> ChildSPtr;

			//	static_assert(!is_array<BaseSPtr::element_type>::value, "This test requires a non-array type");
			//	static_assert(is_convertible<ChildSPtr::pointer, BaseSPtr::pointer>::value, "UniquePtr ptr types must be convertible for this test");
			//	static_assert(is_assignable<BaseSPtr::deleter_type&, ChildSPtr::deleter_type&&>::value, "Deleter types must be assignable to one another");

			//	BaseSPtr ptr(new Base);
			//	EATEST_VERIFY(ptr.get());

			//	unique_ptr<Child> newPtr(new Child);
			//	EATEST_VERIFY(newPtr.get());

			//	ptr = eastl::move(newPtr);
			//	EATEST_VERIFY(ptr);
			//	EATEST_VERIFY(newPtr.get() == nullptr);
			//}
		}
	}

	return nErrorCount;
}


static int Test_scoped_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		EATEST_VERIFY(A::mCount == 0);

		scoped_ptr<int> pT1(new int(5));
		EATEST_VERIFY(*pT1 == 5);

		*pT1 = 3;
		EATEST_VERIFY(*pT1 == 3);
		EATEST_VERIFY(pT1.get() == get_pointer(pT1));

		scoped_ptr<A> pT2(new A(1));
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(A::mCount == 1);

		pT2.reset(new A(2));
		EATEST_VERIFY(pT2->mc == 2);

		pT2.reset(0);
		EATEST_VERIFY(pT2.get() == (A*)0);
		EATEST_VERIFY(pT2.get() == get_pointer(pT2));

		pT2.reset(new A(3));
		EATEST_VERIFY(pT2->mc == 3);

		scoped_ptr<A> pT3(new A(4));
		EATEST_VERIFY(pT3->mc == 4);

		pT2.swap(pT3);
		EATEST_VERIFY(pT2->mc == 4);
		EATEST_VERIFY(pT3->mc == 3);

		swap(pT2, pT3);
		EATEST_VERIFY(pT2->mc == 3);
		EATEST_VERIFY(pT3->mc == 4);
		EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get()));

		scoped_ptr<A> pT4;
		EATEST_VERIFY(pT4.get() == (A*)0);
		if(pT4)
			EATEST_VERIFY(pT4.get()); // Will fail
		if(!(!pT4))
			EATEST_VERIFY(pT4.get()); // Will fail

		pT4.reset(new A(0));
		if(!pT4)
			EATEST_VERIFY(!pT4.get()); // Will fail

		EATEST_VERIFY(A::mCount == 3);
	}

	{   // Test the detach function.
		scoped_ptr<A> ptr(new A);
		A* pA = ptr.detach();
		delete pA;
	}

	{
		scoped_ptr<void> ptr(new int);
		(void)ptr;
	}

	EATEST_VERIFY(A::mCount == 0);

	return nErrorCount;
}



static int Test_scoped_array()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		scoped_array<int> pT1(new int[5]);
		pT1[0] = 5;
		EATEST_VERIFY(pT1[0] == 5);
		EATEST_VERIFY(pT1.get()[0] == 5);

		scoped_array<A> pT2(new A[2]);
		EATEST_VERIFY(A::mCount == 2);
		EATEST_VERIFY(pT2[0].mc == 0);
		EATEST_VERIFY(pT2.get()[0].mc == 0);
		EATEST_VERIFY(get_pointer(pT2)[0].mc == 0);

		pT2.reset(new A[4]);
		EATEST_VERIFY(A::mCount == 4);
		if(!pT2)
			EATEST_VERIFY(!pT2.get()); // Will fail

		pT2.reset(0);
		EATEST_VERIFY(A::mCount == 0);
		if(pT2)
			EATEST_VERIFY(pT2.get()); // Will fail
		if(!(!pT2))
			EATEST_VERIFY(pT2.get()); // Will fail

		scoped_array<A> pT3(new A[3]);
		EATEST_VERIFY(A::mCount == 3);

		pT2.swap(pT3);
		EATEST_VERIFY(A::mCount == 3);

		swap(pT2, pT3);
		EATEST_VERIFY(A::mCount == 3);
		EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get()));

		EATEST_VERIFY(A::mCount == 3);
	}

	{   // Test the detach function.
		scoped_array<A> ptr(new A[6]);
		A* pArray = ptr.detach();
		delete[] pArray;
	}

	{
		scoped_array<void> ptr(new int[6]);
		(void)ptr;
	}

	EATEST_VERIFY(A::mCount == 0);

	return nErrorCount;
}


static int Test_shared_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	// Name test.
	#if EASTLTEST_GETTYPENAME_AVAILABLE
		//eastl::string sTypeName = GetTypeName<typename eastl::unique_ptr<int>::pointer>();
		//EA::UnitTest::Report("type name of (typename shared_ptr<int>::pointer): %s", sTypeName.c_str());

		//sTypeName = GetTypeName<typename eastl::common_type<int*, int*>::type>();
		//EA::UnitTest::Report("type name of (typename eastl::common_type<int*, int*>::type): %s", sTypeName.c_str());
	#endif

	{
		shared_ptr<int> pT1;
		EATEST_VERIFY(pT1.get() == NULL);
	}

	{
		shared_ptr<int> pT1(new int(5));
		EATEST_VERIFY(*pT1 == 5);
		EATEST_VERIFY(pT1.get() == get_pointer(pT1));
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1.unique() );

		shared_ptr<int> pT2;
		EATEST_VERIFY(pT1 != pT2);
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1.unique());

		pT2 = pT1;
		EATEST_VERIFY(pT1.use_count() == 2);
		EATEST_VERIFY(pT2.use_count() == 2);
		EATEST_VERIFY(!pT1.unique());
		EATEST_VERIFY(!(pT1 < pT2)); // They should be equal
		EATEST_VERIFY(pT1 == pT2);

		*pT1 = 3;
		EATEST_VERIFY(*pT1 == 3);
		EATEST_VERIFY(*pT1 == 3);
		EATEST_VERIFY(*pT2 == 3);

		pT2.reset((int*)NULL);
		EATEST_VERIFY(pT2.unique());
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT1.unique());
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1 != pT2);
	}

	{
		EATEST_VERIFY(A::mCount == 0);

		shared_ptr<A> pT2(new A(0));
		EATEST_VERIFY(A::mCount == 1);
		EATEST_VERIFY(pT2->mc == 0);
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());

		pT2.reset(new A(1));
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(A::mCount == 1);
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());

		shared_ptr<A> pT3(new A(2));
		EATEST_VERIFY(A::mCount == 2);

		pT2.swap(pT3);
		EATEST_VERIFY(pT2->mc == 2);
		EATEST_VERIFY(pT3->mc == 1);
		EATEST_VERIFY(A::mCount == 2);

		swap(pT2, pT3);
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(pT3->mc == 2);
		EATEST_VERIFY(A::mCount == 2);
		if(!pT2)
			EATEST_VERIFY(!pT2.get()); // Will fail

		shared_ptr<A> pT4;
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());
		EATEST_VERIFY(A::mCount == 2);
		if(pT4)
			EATEST_VERIFY(pT4.get()); // Will fail
		if(!(!pT4))
			EATEST_VERIFY(pT4.get()); // Will fail

		pT4 = pT2;
		EATEST_VERIFY(pT2.use_count() == 2);
		EATEST_VERIFY(pT4.use_count() == 2);
		EATEST_VERIFY(!pT2.unique());
		EATEST_VERIFY(!pT4.unique());
		EATEST_VERIFY(A::mCount == 2);
		EATEST_VERIFY(pT2 == pT4);
		EATEST_VERIFY(pT2 != pT3);
		EATEST_VERIFY(!(pT2 < pT4)); // They should be equal

		shared_ptr<A> pT5(pT4);
		EATEST_VERIFY(pT4 == pT5);
		EATEST_VERIFY(pT2.use_count() == 3);
		EATEST_VERIFY(pT4.use_count() == 3);
		EATEST_VERIFY(pT5.use_count() == 3);
		EATEST_VERIFY(!pT5.unique());

		pT4 = shared_ptr<A>((A*)NULL);
		EATEST_VERIFY(pT4.unique());
		EATEST_VERIFY(pT4.use_count() == 1);
		EATEST_VERIFY(pT2.use_count() == 2);

		EATEST_VERIFY(A::mCount == 2);
	}


	// Regression test reported by a user.
	// typename eastl::enable_if<!eastl::is_array<U>::value && eastl::is_convertible<U*, element_type*>::value, this_type&>::type
	// operator=(unique_ptr<U, Deleter> && uniquePtr)
	{
		{
			shared_ptr<A> rT1(new A(42));
			unique_ptr<B> rT2(new B);  // default ctor uses 0
			rT2->mc = 115;

			EATEST_VERIFY(rT1->mc == 42);
			EATEST_VERIFY(rT2->mc == 115);

			rT1 = eastl::move(rT2);

			EATEST_VERIFY(rT1->mc == 115);
			// EATEST_VERIFY(rT2->mc == 115);  // state of object post-move is undefined.
		}

		// test the state of the shared_ptr::operator= return
		{
			shared_ptr<A> rT1(new A(42));
			unique_ptr<B> rT2(new B);  // default ctor uses 0
			rT2->mc = 115;

			shared_ptr<A> operatorReturn = (rT1 = eastl::move(rT2));

			EATEST_VERIFY(operatorReturn == rT1);

			EATEST_VERIFY(operatorReturn->mc == 115);
			// EATEST_VERIFY(rT1->mc == 115); // implied as both are pointing to the same address
		}
	}


	{ // Test member template functions.
		shared_ptr<ChildClass>      pCC(new GrandChildClass);
		shared_ptr<ParentClass>     pPC(pCC);
		shared_ptr<GrandChildClass> pGCC(static_pointer_cast<GrandChildClass>(pPC));
	}


	{ // Test enable_shared_from_this
		shared_ptr<Y> p(new Y);
		shared_ptr<Y> q = p->f();

		EATEST_VERIFY(p == q);
		EATEST_VERIFY(!(p < q || q < p)); // p and q must share ownership

		shared_ptr<BCLS> bctrlp = shared_ptr<BCLS>(new BCLS);
	}


	{ // Test static_pointer_cast, etc.
		shared_ptr<GrandChildClass> pGCC(new GrandChildClass);
		shared_ptr<ParentClass>     pPC = static_pointer_cast<ParentClass>(pGCC);

		EATEST_VERIFY(pPC == pGCC);

		#if EASTL_RTTI_ENABLED
		shared_ptr<ChildClass>      pCC = dynamic_pointer_cast<ChildClass>(pPC);
		EATEST_VERIFY(pCC == pGCC);
		#endif

		#if !defined(__GNUC__) ||  (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)...
			eastl::shared_ptr<const void> pVoidPtr = shared_ptr<ParentClass>(new ParentClass);
			shared_ptr<ParentClass> ap = const_pointer_cast<ParentClass>(static_pointer_cast<const ParentClass>(pVoidPtr));
		#endif

		//typedef shared_ptr<void const> ASPtr;
		//shared_ptr<void const> pVoidPtr = ASPtr(new ParentClass);
		//ASPtr ap = const_pointer_cast<ParentClass>(static_pointer_cast<const ParentClass>(pVoidPtr));
	}


	{ // Test static_shared_pointer_cast, etc.
		shared_ptr<GrandChildClass> pGCC(new GrandChildClass);
		shared_ptr<ParentClass>     pPC = static_shared_pointer_cast<ParentClass /*, EASTLAllocatorType, smart_ptr_deleter<ParentClass>*/ >(pGCC);

		EATEST_VERIFY(pPC == pGCC);

		#if EASTL_RTTI_ENABLED
		shared_ptr<ChildClass>      pCC = dynamic_shared_pointer_cast<ChildClass /*, EASTLAllocatorType, smart_ptr_deleter<ParentClass>*/ >(pPC);
		EATEST_VERIFY(pCC == pGCC);
		#endif
	}


	{ // Test smart_ptr_deleter
		shared_ptr<void> pVoid(new ParentClass, smart_ptr_deleter<ParentClass>());
		EATEST_VERIFY(pVoid.get() != NULL);

		pVoid = shared_ptr<ParentClass>(new ParentClass, smart_ptr_deleter<ParentClass>());
		EATEST_VERIFY(pVoid.get() != NULL);
	}


	{ // Test shared_ptr lambda deleter
		auto deleter = [](int*) {}; 
		eastl::shared_ptr<int> ptr(nullptr, deleter);

		EATEST_VERIFY(!ptr);
		EATEST_VERIFY(ptr.get() == nullptr);
	}


	{ // Test of shared_ptr<void const>
		#if !defined(__GNUC__) ||  (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)...
			shared_ptr<void const> voidPtr = shared_ptr<A1>(new A1);
			shared_ptr<A1>         a1Ptr   = const_pointer_cast<A1>(static_pointer_cast<const A1>(voidPtr));
		#endif
	}


	{ // Test of static_pointer_cast
		shared_ptr<B1> bPtr = shared_ptr<B1>(new B1);
		shared_ptr<A1> aPtr = static_pointer_cast<A1, B1>(bPtr);
	}


	{ // Test shared_ptr<void>
		{
			#if !defined(__GNUC__) ||  (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)...
				const char* const pName = "NamedClassTest";

				NamedClass* const pNamedClass0 = new NamedClass(pName);
				EATEST_VERIFY(pNamedClass0->mpName == pName);

			  //shared_ptr<void const, EASTLAllocatorType, smart_ptr_deleter<NamedClass> > voidPtr(pNamedClass0);
				shared_ptr<void const> voidPtr(pNamedClass0);
				EATEST_VERIFY(voidPtr.get() == pNamedClass0);

				NamedClass* const pNamedClass1 = (NamedClass*)voidPtr.get();
				EATEST_VERIFY(pNamedClass1->mpName == pName);
			#endif
		}

		{
			#if !defined(__GNUC__) ||  (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)...
				const char* const pName = "NamedClassTest";

				NamedClass* const pNamedClass0 = new NamedClass(pName);
				EATEST_VERIFY(pNamedClass0->mpName == pName);

				shared_ptr<void const> voidPtr(pNamedClass0, smart_ptr_deleter<NamedClass>());
				EATEST_VERIFY(voidPtr.get() == pNamedClass0);

				NamedClass* const pNamedClass1 = (NamedClass*)voidPtr.get();
				EATEST_VERIFY(pNamedClass1->mpName == pName);
			#endif
		}
	}


	{
		const char* const pName1 = "NamedClassTest1";
		const char* const pName2 = "NamedClassTest2";

		shared_ptr<NamedClass> sp(new NamedClass(pName1));
		EATEST_VERIFY(!sp == false);
		EATEST_VERIFY(sp.unique());
		EATEST_VERIFY(sp->mpName == pName1);

		shared_ptr<NamedClass> sp2 = sp;
		EATEST_VERIFY(sp2.use_count() == 2);

		sp2.reset(new NamedClass(pName2));
		EATEST_VERIFY(sp2.use_count() == 1);
		EATEST_VERIFY(sp.unique());
		EATEST_VERIFY(sp2->mpName == pName2);

		sp.reset();
		EATEST_VERIFY(!sp == true);
	}

	{
		// Exception handling tests
		#if EASTL_EXCEPTIONS_ENABLED
			try {
				weak_ptr<A>   pWeakA;             // leave uninitalized
				shared_ptr<A> pSharedA(pWeakA);   // This should throw eastl::bad_weak_ptr
				EATEST_VERIFY(false);
			}
			catch(eastl::bad_weak_ptr&)
			{
				EATEST_VERIFY(true);            // This pathway should be taken.
			}
			catch(...)
			{
				EATEST_VERIFY(false);
			}


			ThrowingAllocator<true> throwingAllocator; // Throw on first attempt to allocate.
			shared_ptr<A> pA0;

			try {
				A::mCount = 0;
				pA0 = eastl::allocate_shared<A, ThrowingAllocator<true> >(throwingAllocator, 'a');
				EATEST_VERIFY(false);
			}
			catch(std::bad_alloc&)
			{
				EATEST_VERIFY(true);                // This pathway should be taken.
				EATEST_VERIFY(pA0.get() == NULL);   // The C++11 Standard doesn't seem to require this, but that's how we currently do it until we learn it should be otherwise.
				EATEST_VERIFY(pA0.use_count() == 0);
				EATEST_VERIFY(A::mCount == 0);      // Verify that there were no surviving A instances since the exception.
			}
			catch(...)
			{
				EATEST_VERIFY(false);
			}


			try {
				shared_ptr<A> pA1(new A('a'), default_delete<A>(), throwingAllocator);
				EATEST_VERIFY(false);
			}
			catch(std::bad_alloc&)
			{
				EATEST_VERIFY(true);                // This pathway should be taken.
				EATEST_VERIFY(A::mCount == 0);
			}
			catch(...)
			{
				EATEST_VERIFY(false);
			}

		#endif

	}

	#if EASTL_RTTI_ENABLED
	{
		// template <typename U, typename A, typename D>
		// shared_ptr(const shared_ptr<U, A, D>& sharedPtr, dynamic_cast_tag);
		// To do.

		// template <typename U, typename A, typename D, typename UDeleter>
		// shared_ptr(const shared_ptr<U, A, D>& sharedPtr, dynamic_cast_tag, const UDeleter&);
		// To do.
	}
	#endif

	EATEST_VERIFY(A::mCount == 0);

	return nErrorCount;
}




#if EASTL_THREAD_SUPPORT_AVAILABLE
	// C++ Standard section 20.7.2.5 -- shared_ptr atomic access
	// shared_ptr thread safety is about safe use of the pointer itself and not about what it points to. shared_ptr thread safety
	// allows you to safely use shared_ptr from different threads, but if the object shared_ptr holds requires thread safety then
	// you need to separately handle that in a thread-safe way. A good way to think about it is this: "shared_ptr is as thread-safe as a raw pointer."
	//
	// Some helper links:
	//     http://stackoverflow.com/questions/9127816/stdshared-ptr-thread-safety-explained
	//     http://stackoverflow.com/questions/14482830/stdshared-ptr-thread-safety
	//     http://cppwisdom.quora.com/shared_ptr-is-almost-thread-safe
	// 

	// Test the ability of Futex to report the callstack of another thread holding a futex.
	struct SharedPtrTestThread : public EA::Thread::IRunnable
	{
		EA::Thread::ThreadParameters    mThreadParams;
		EA::Thread::Thread              mThread;
		eastl::atomic<bool>             mbShouldContinue;
		int                             mnErrorCount;
		eastl::shared_ptr<TestObject>*  mpSPTO;
		eastl::weak_ptr<TestObject>*    mpWPTO;

		SharedPtrTestThread() : mThreadParams(), mThread(), mbShouldContinue(true), mnErrorCount(0), mpSPTO(NULL), mpWPTO(NULL) {}
		SharedPtrTestThread(const SharedPtrTestThread&){}
		void operator=(const SharedPtrTestThread&){}

		intptr_t Run(void*)
		{
			int& nErrorCount = mnErrorCount; // declare nErrorCount so that EATEST_VERIFY can work, as it depends on it being declared.

			while(mbShouldContinue.load(eastl::memory_order_relaxed))
			{
				EA::UnitTest::ThreadSleepRandom(1, 10);

				EATEST_VERIFY(mpSPTO->get()->mX == 99);

				eastl::shared_ptr<TestObject> temp(mpWPTO->lock());
				EATEST_VERIFY(temp->mX == 99);

				eastl::shared_ptr<TestObject> spTO2(*mpSPTO);
				EATEST_VERIFY(spTO2->mX == 99);
				EATEST_VERIFY(spTO2.use_count() >= 2);

				eastl::weak_ptr<TestObject> wpTO2(spTO2);
				temp = mpWPTO->lock();
				EATEST_VERIFY(temp->mX == 99);

				temp = spTO2;
				spTO2.reset();
				EATEST_VERIFY(mpSPTO->get()->mX == 99);
			}

			return nErrorCount;
		}
	};
#endif


static int Test_shared_ptr_thread()
{
	using namespace SmartPtrTest;
	using namespace eastl;
	using namespace EA::Thread;

	int nErrorCount(0);

	#if EASTL_THREAD_SUPPORT_AVAILABLE
		{
			SharedPtrTestThread    thread[4];
			shared_ptr<TestObject> spTO(new TestObject(99));
			weak_ptr<TestObject>   wpTO(spTO);

			for(size_t i = 0; i < EAArrayCount(thread); i++)
			{
				thread[i].mpSPTO = &spTO;
				thread[i].mpWPTO = &wpTO;
				thread[i].mThreadParams.mpName = "SharedPtrTestThread";
			}

			for(size_t i = 0; i < EAArrayCount(thread); i++)
				thread[i].mThread.Begin(&thread[0], NULL, &thread[0].mThreadParams);

			EA::UnitTest::ThreadSleep(2000);

			for(size_t i = 0; i < EAArrayCount(thread); i++)
				thread[i].mbShouldContinue.store(false, eastl::memory_order_relaxed);

			for(size_t i = 0; i < EAArrayCount(thread); i++)
			{
				thread[i].mThread.WaitForEnd();
				nErrorCount += thread[i].mnErrorCount;
			}
		}
	#endif

	#if EASTL_THREAD_SUPPORT_AVAILABLE
		{
			// We currently do light testing of the atomic functions. It would take a bit of work to fully test
			// the memory behavior of these in a rigorous way. Also, as of this writing we don't have a portable
			// way to use the std::memory_order functionality.

			shared_ptr<TestObject> spTO(new TestObject(55));

			// bool atomic_is_lock_free(const shared_ptr<T>*);
			EATEST_VERIFY(!atomic_is_lock_free(&spTO));

			// shared_ptr<T> atomic_load(const shared_ptr<T>* pSharedPtr);
			// shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* pSharedPtr, ... /*std::memory_order memoryOrder*/);
			shared_ptr<TestObject> spTO2 = atomic_load(&spTO);
			EATEST_VERIFY(spTO->mX == 55);
			EATEST_VERIFY(spTO2->mX == 55);

			// void atomic_store(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB);
			// void atomic_store_explicit(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB, ... /*std::memory_order memoryOrder*/);
			spTO2->mX = 56;
			EATEST_VERIFY(spTO->mX == 56);
			EATEST_VERIFY(spTO2->mX == 56);

			atomic_store(&spTO, shared_ptr<TestObject>(new TestObject(77)));
			EATEST_VERIFY(spTO->mX == 77);
			EATEST_VERIFY(spTO2->mX == 56);

			// shared_ptr<T> atomic_exchange(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB);
			// shared_ptr<T> atomic_exchange_explicit(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB, ... /*std::memory_order memoryOrder*/);
			spTO = atomic_exchange(&spTO2, spTO);
			EATEST_VERIFY(spTO->mX == 56);
			EATEST_VERIFY(spTO2->mX == 77);
			
			spTO = atomic_exchange_explicit(&spTO2, spTO);
			EATEST_VERIFY(spTO->mX == 77);
			EATEST_VERIFY(spTO2->mX == 56);

			// bool atomic_compare_exchange_strong(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew);
			// bool atomic_compare_exchange_weak(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew);
			// bool atomic_compare_exchange_strong_explicit(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew, ... /*memory_order memoryOrderSuccess, memory_order memoryOrderFailure*/);
			// bool atomic_compare_exchange_weak_explicit(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew, ... /*memory_order memoryOrderSuccess, memory_order memoryOrderFailure*/);
			shared_ptr<TestObject> spTO3 = atomic_load(&spTO2);
			bool result = atomic_compare_exchange_strong(&spTO3, &spTO, make_shared<TestObject>(88));   // spTO3 != spTO, so this should do no exchange and return false.
			EATEST_VERIFY(!result);
			EATEST_VERIFY(spTO3->mX == 56);
			EATEST_VERIFY(spTO->mX == 56);

			result = atomic_compare_exchange_strong(&spTO3, &spTO2, make_shared<TestObject>(88));       // spTO3 == spTO2, so this should succeed.
			EATEST_VERIFY(result);
			EATEST_VERIFY(spTO2->mX == 56);
			EATEST_VERIFY(spTO3->mX == 88);
		}
	#endif

	EATEST_VERIFY(A::mCount == 0);
	TestObject::Reset();

	return nErrorCount;
}


static int Test_weak_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		weak_ptr<int>   pW0;
		shared_ptr<int> pS0(new int(0));
		shared_ptr<int> pS1(new int(1));
		weak_ptr<int>   pW1(pS1);
		weak_ptr<int>   pW2;
		weak_ptr<int>   pW3(pW2);

		EATEST_VERIFY(pS1.use_count() == 1);
		EATEST_VERIFY(pW1.use_count() == 1);
		EATEST_VERIFY(pW2.use_count() == 0);
		EATEST_VERIFY(pW3.use_count() == 0);
		EATEST_VERIFY(pW1.expired() == false);
		EATEST_VERIFY(pW2.expired() == true);
		EATEST_VERIFY(pW3.expired() == true);
		pS1.reset();
		EATEST_VERIFY(pW1.expired() == true);
		pW1 = pS0;
		EATEST_VERIFY(pW1.expired() == false);
		pW1.swap(pW2);
		EATEST_VERIFY(pW1.expired() == true);
		EATEST_VERIFY(pW2.expired() == false);
		pW1 = pW2;
		EATEST_VERIFY(pW1.expired() == false);
		pW3 = pW1;
		EATEST_VERIFY(pW3.expired() == false);
		EATEST_VERIFY(pS1.use_count() == 0);
		pW3.reset();
		EATEST_VERIFY(pW3.expired() == true);
		pS1.reset(new int(3));
		EATEST_VERIFY(pS1.use_count() == 1);
		pW3 = pS1;
		EATEST_VERIFY(pS1.use_count() == 1);
		EATEST_VERIFY(pS1.use_count() == pW3.use_count());

		shared_ptr<int> pShared2(pW2.lock());
		shared_ptr<int> pShared3(pW3.lock());

		EATEST_VERIFY(pShared2.use_count() == 2);
		EATEST_VERIFY(pShared3.use_count() == 2); 
		swap(pW2, pW3);
		EATEST_VERIFY(pW2.use_count() == 2);
		EATEST_VERIFY(pW3.use_count() == 2);
		pW1 = pW3;
		EATEST_VERIFY(pW3.use_count() == 2);

		EATEST_VERIFY((pW2 < pW3) || (pW3 < pW2));

		EATEST_VERIFY(pS0.use_count() == 2);
		pW0 = pS0; // This tests the deletion of a weak_ptr after its associated shared_ptr has destructed.
		EATEST_VERIFY(pS0.use_count() == 2);
	}


	{
		weak_ptr<NamedClass> wp;

		EATEST_VERIFY(wp.use_count() == 0);
		EATEST_VERIFY(wp.expired() == true);

		{
			shared_ptr<NamedClass> sp(new NamedClass("NamedClass"));
			wp = sp;

			EATEST_VERIFY(wp.use_count() == 1);
			EATEST_VERIFY(wp.expired() == false);
		}

		EATEST_VERIFY(wp.use_count() == 0);
		EATEST_VERIFY(wp.expired() == true);
	}

	{   // shared_from_this
		// This example is taken from the C++11 Standard doc.
		shared_ptr<const foo> pFoo(new foo);
		shared_ptr<const foo> qFoo = pFoo->shared_from_this();

		EATEST_VERIFY(pFoo == qFoo);
		EATEST_VERIFY(!(pFoo < qFoo) && !(qFoo < pFoo)); // p and q share ownership
	}

	{   // weak_from_this const
		shared_ptr<const foo> pFoo(new foo);
		weak_ptr<const foo> qFoo = pFoo->weak_from_this();

		EATEST_VERIFY(pFoo == qFoo.lock());
		EATEST_VERIFY(!(pFoo < qFoo.lock()) && !(qFoo.lock() < pFoo)); // p and q share ownership
	}

	{   // weak_from_this
		shared_ptr<foo> pFoo(new foo);
		weak_ptr<foo> qFoo = pFoo->weak_from_this();

		EATEST_VERIFY(pFoo == qFoo.lock());
		EATEST_VERIFY(!(pFoo < qFoo.lock()) && !(qFoo.lock() < pFoo)); // p and q share ownership
	}
	
	return nErrorCount;
}


static int Test_shared_array()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		shared_array<int> pT1(new int[5]);
		pT1[0] = 5;
		EATEST_VERIFY(pT1[0] == 5);
		EATEST_VERIFY(pT1.get() == get_pointer(pT1));
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1.unique());

		shared_array<int> pT2;
		EATEST_VERIFY(pT1 != pT2);
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1.unique());

		pT2 = pT1;
		EATEST_VERIFY(pT1.use_count() == 2);
		EATEST_VERIFY(pT2.use_count() == 2);
		EATEST_VERIFY(!pT1.unique());
		EATEST_VERIFY(!(pT1 < pT2)); // They should be equal
		EATEST_VERIFY(pT1 == pT2);

		*pT1 = 3;
		EATEST_VERIFY(*pT1 == 3);
		EATEST_VERIFY(*pT1 == 3);
		EATEST_VERIFY(*pT2 == 3);

		pT2.reset(0);
		EATEST_VERIFY(pT2.unique());
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT1.unique());
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1 != pT2);
	}

	{
		EATEST_VERIFY(A::mCount == 0);

		shared_array<A> pT2(new A[5]);
		EATEST_VERIFY(A::mCount == 5);
		EATEST_VERIFY(pT2->mc == 0);
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());

		pT2.reset(new A[1]);
		pT2[0].mc = 1;
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(A::mCount == 1);
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());

		shared_array<A> pT3(new A[2]);
		EATEST_VERIFY(A::mCount == 3);

		pT2.swap(pT3);
		pT2[0].mc = 2;
		EATEST_VERIFY(pT2->mc == 2);
		EATEST_VERIFY(pT3->mc == 1);
		EATEST_VERIFY(A::mCount == 3);

		swap(pT2, pT3);
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(pT3->mc == 2);
		EATEST_VERIFY(A::mCount == 3);
		if(!pT2)
			EATEST_VERIFY(!pT2.get()); // Will fail

		shared_array<A> pT4;
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());
		EATEST_VERIFY(A::mCount == 3);
		if(pT4)
			EATEST_VERIFY(pT4.get()); // Will fail
		if(!(!pT4))
			EATEST_VERIFY(pT4.get()); // Will fail

		pT4 = pT2;
		EATEST_VERIFY(pT2.use_count() == 2);
		EATEST_VERIFY(pT4.use_count() == 2);
		EATEST_VERIFY(!pT2.unique());
		EATEST_VERIFY(!pT4.unique());
		EATEST_VERIFY(A::mCount == 3);
		EATEST_VERIFY(pT2 == pT4);
		EATEST_VERIFY(pT2 != pT3);
		EATEST_VERIFY(!(pT2 < pT4)); // They should be equal

		shared_array<A> pT5(pT4);
		EATEST_VERIFY(pT4 == pT5);
		EATEST_VERIFY(pT2.use_count() == 3);
		EATEST_VERIFY(pT4.use_count() == 3);
		EATEST_VERIFY(pT5.use_count() == 3);
		EATEST_VERIFY(!pT5.unique());

		pT4 = shared_array<A>(0);
		EATEST_VERIFY(pT4.unique());
		EATEST_VERIFY(pT4.use_count() == 1);
		EATEST_VERIFY(pT2.use_count() == 2);

		EATEST_VERIFY(A::mCount == 3);
	}

	EATEST_VERIFY(A::mCount == 0);

	return nErrorCount;
}



static int Test_linked_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		linked_ptr<int> pT1(new int(5));
		EATEST_VERIFY(*pT1.get() == 5);
		EATEST_VERIFY(pT1.get() == get_pointer(pT1));
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1.unique());

		linked_ptr<int> pT2;
		EATEST_VERIFY(pT1 != pT2);
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1.unique());

		pT2 = pT1;
		EATEST_VERIFY(pT1.use_count() == 2);
		EATEST_VERIFY(pT2.use_count() == 2);
		EATEST_VERIFY(!pT1.unique());
		EATEST_VERIFY(!(pT1 < pT2)); // They should be equal
		EATEST_VERIFY(pT1 == pT2);

		*pT1 = 3;
		EATEST_VERIFY(*pT1.get() == 3);
		EATEST_VERIFY(*pT1 == 3);
		EATEST_VERIFY(*pT2 == 3);

		pT2.reset((int*)NULL);
		EATEST_VERIFY(pT2.unique());
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT1.unique());
		EATEST_VERIFY(pT1.use_count() == 1);
		EATEST_VERIFY(pT1 != pT2);
	}

	{
		EATEST_VERIFY(A::mCount == 0);

		linked_ptr<A> pT2(new A(0));
		EATEST_VERIFY(A::mCount == 1);
		EATEST_VERIFY(pT2->mc == 0);
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());

		pT2.reset(new A(1));
		EATEST_VERIFY(pT2->mc == 1);
		EATEST_VERIFY(A::mCount == 1);
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());

		linked_ptr<A> pT3(new A(2));
		EATEST_VERIFY(A::mCount == 2);

		linked_ptr<A> pT4;
		EATEST_VERIFY(pT2.use_count() == 1);
		EATEST_VERIFY(pT2.unique());
		EATEST_VERIFY(A::mCount == 2);
		if(pT4)
			EATEST_VERIFY(pT4.get()); // Will fail
		if(!(!pT4))
			EATEST_VERIFY(pT4.get()); // Will fail

		pT4 = pT2;
		EATEST_VERIFY(pT2.use_count() == 2);
		EATEST_VERIFY(pT4.use_count() == 2);
		EATEST_VERIFY(!pT2.unique());
		EATEST_VERIFY(!pT4.unique());
		EATEST_VERIFY(A::mCount == 2);
		EATEST_VERIFY(pT2 == pT4);
		EATEST_VERIFY(pT2 != pT3);
		EATEST_VERIFY(!(pT2 < pT4)); // They should be equal

		linked_ptr<A> pT5(pT4);
		EATEST_VERIFY(pT4 == pT5);
		EATEST_VERIFY(pT2.use_count() == 3);
		EATEST_VERIFY(pT4.use_count() == 3);
		EATEST_VERIFY(pT5.use_count() == 3);
		EATEST_VERIFY(!pT5.unique());

		pT4 = linked_ptr<A>((A*)NULL);
		EATEST_VERIFY(pT4.unique());
		EATEST_VERIFY(pT4.use_count() == 1);
		EATEST_VERIFY(pT2.use_count() == 2);

		EATEST_VERIFY(A::mCount == 2);
	}
		
	{  // Do some force_delete tests.
		linked_ptr<A> pT2(new A(0));
		linked_ptr<A> pT3(pT2);
		pT2.force_delete();
		pT3.force_delete();
	}

	EATEST_VERIFY(A::mCount == 0);


	{   // Verify that subclasses are usable.
		bool bAlloc = false;

		eastl::linked_ptr<DerivedMockObject> pDMO(new DerivedMockObject(&bAlloc));
		eastl::linked_ptr<MockObject> a1(pDMO);
		eastl::linked_ptr<MockObject> a2;

		a2 = pDMO;
	}

	{ // Test regression for a bug.
		linked_ptr<A> pT2;
		linked_ptr<A> pT3(pT2);     // In the bug linked_ptr::mpPrev and mpNext were not initialized via this ctor.
		pT3.reset(new A);           // In the bug this would crash due to unintialized mpPrev/mpNext.

		linked_ptr<B> pT4;
		linked_ptr<A> pT5(pT4);
		pT5.reset(new A);

		linked_array<A> pT6;
		linked_array<A> pT7(pT6);
		pT7.reset(new A[1]);
	}

	return nErrorCount;
}



static int Test_linked_array()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount(0);

	{
		// Tests go here.
	}

	{  // Do some force_delete tests.
		linked_array<A> pT2(new A[2]);
		linked_array<A> pT3(pT2);
		pT2.force_delete();
		pT3.force_delete();
	}

	EATEST_VERIFY(A::mCount == 0);


	return nErrorCount;
}



static int Test_intrusive_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount = 0;

	{   // Test ctor/dtor
		intrusive_ptr<RefCountTest> ip1;
		intrusive_ptr<RefCountTest> ip2(NULL, false);
		intrusive_ptr<RefCountTest> ip3(NULL, true);
		intrusive_ptr<RefCountTest> ip4(new RefCountTest, true);
		intrusive_ptr<RefCountTest> ip5(new RefCountTest, false);
		intrusive_ptr<RefCountTest> ip6(ip1);
		intrusive_ptr<RefCountTest> ip7(ip4);

		EATEST_VERIFY(ip1.get() == NULL);
		EATEST_VERIFY(!ip1);

		EATEST_VERIFY(ip2.get() == NULL);
		EATEST_VERIFY(!ip2);

		EATEST_VERIFY(ip3.get() == NULL);
		EATEST_VERIFY(!ip3);

		EATEST_VERIFY(ip4.get() != NULL);
		EATEST_VERIFY(ip4.get()->mRefCount == 2);
		EATEST_VERIFY(ip4);

		EATEST_VERIFY(ip5.get() != NULL);
		EATEST_VERIFY(ip5.get()->mRefCount == 0);
		ip5.get()->AddRef();
		EATEST_VERIFY(ip5.get()->mRefCount == 1);
		EATEST_VERIFY(ip5);

		EATEST_VERIFY(ip6.get() == NULL);
		EATEST_VERIFY(!ip6);

		EATEST_VERIFY(ip7.get() != NULL);
		EATEST_VERIFY(ip7.get()->mRefCount == 2);
		EATEST_VERIFY(ip7);
	}

	{ 
		// Test move-ctor
		{
			VERIFY(RefCountTest::mCount == 0);
			intrusive_ptr<RefCountTest> ip1(new RefCountTest);
			VERIFY(RefCountTest::mCount == 1);
			VERIFY(ip1->mRefCount == 1);
			{
				intrusive_ptr<RefCountTest> ip2(eastl::move(ip1));
				VERIFY(ip1.get() != ip2.get());
				VERIFY(ip2->mRefCount == 1);
				VERIFY(RefCountTest::mCount == 1);
			}
			VERIFY(ip1.get() == nullptr);
			VERIFY(RefCountTest::mCount == 0);
		}

		// Test move-assignment
		{
			VERIFY(RefCountTest::mCount == 0);
			intrusive_ptr<RefCountTest> ip1(new RefCountTest);
			VERIFY(RefCountTest::mCount == 1);
			VERIFY(ip1->mRefCount == 1);
			{
				intrusive_ptr<RefCountTest> ip2; 
				ip2 = eastl::move(ip1);
				VERIFY(ip1.get() != ip2.get());
				VERIFY(ip2->mRefCount == 1);
				VERIFY(RefCountTest::mCount == 1);
			}
			VERIFY(ip1.get() == nullptr);
			VERIFY(RefCountTest::mCount == 0);
		}
	}

	{   // Test modifiers (assign, attach, detach, reset, swap)
		RefCountTest* const p1 = new RefCountTest;
		RefCountTest* const p2 = new RefCountTest;
		intrusive_ptr<RefCountTest> ip1;
		intrusive_ptr<RefCountTest> ip2;

		ip1 = p1;
		ip2 = p2;
		EATEST_VERIFY(ip1.get() == p1);
		EATEST_VERIFY((*ip1).mRefCount == 1);
		EATEST_VERIFY(ip1->mRefCount == 1);
		ip1.detach();
		EATEST_VERIFY(ip1.get() == NULL);
		ip1.attach(p1);
		EATEST_VERIFY(ip1.get() == p1);
		EATEST_VERIFY(ip1->mRefCount == 1);
		ip1.swap(ip2);
		EATEST_VERIFY(ip1.get() == p2);
		EATEST_VERIFY(ip2.get() == p1);
		ip1.swap(ip2);
		ip1 = ip2;
		EATEST_VERIFY(ip1 == p2);
		ip1.reset();
		EATEST_VERIFY(ip1.get() == NULL);
		EATEST_VERIFY(ip2.get() == p2);
		ip2.reset();
		EATEST_VERIFY(ip2.get() == NULL);
	}

	{   // Test external functions
		intrusive_ptr<RefCountTest> ip1;
		intrusive_ptr<RefCountTest> ip2(new RefCountTest);
		intrusive_ptr<RefCountTest> ip3(ip1);
		intrusive_ptr<RefCountTest> ip4(ip2);

		// The VC++ code scanner crashes when it scans this code. 
		EATEST_VERIFY(get_pointer(ip1) == NULL);
		EATEST_VERIFY(get_pointer(ip2) != NULL);
		EATEST_VERIFY(get_pointer(ip3) == get_pointer(ip1));
		EATEST_VERIFY(get_pointer(ip4) == get_pointer(ip2));

		EATEST_VERIFY(ip3 == ip1);
		EATEST_VERIFY(ip4 == ip2);
		EATEST_VERIFY(ip1 == ip3);
		EATEST_VERIFY(ip2 == ip4);

		EATEST_VERIFY(ip1 != ip2);
		EATEST_VERIFY(ip3 != ip4);
		EATEST_VERIFY(ip2 != ip1);
		EATEST_VERIFY(ip4 != ip3);

		EATEST_VERIFY(ip3 == ip1.get());
		EATEST_VERIFY(ip4 == ip2.get());
		EATEST_VERIFY(ip1 == ip3.get());
		EATEST_VERIFY(ip2 == ip4.get());

		EATEST_VERIFY(ip1 != ip2.get());
		EATEST_VERIFY(ip3 != ip4.get());
		EATEST_VERIFY(ip2 != ip1.get());
		EATEST_VERIFY(ip4 != ip3.get());

		EATEST_VERIFY(ip3.get() == ip1);
		EATEST_VERIFY(ip4.get() == ip2);
		EATEST_VERIFY(ip1.get() == ip3);
		EATEST_VERIFY(ip2.get() == ip4);

		EATEST_VERIFY(ip1.get() != ip2);
		EATEST_VERIFY(ip3.get() != ip4);
		EATEST_VERIFY(ip2.get() != ip1);
		EATEST_VERIFY(ip4.get() != ip3);

		EATEST_VERIFY((ip4 < ip3) || (ip3 < ip4));

		swap(ip1, ip3);
		EATEST_VERIFY(get_pointer(ip3) == get_pointer(ip1));

		swap(ip2, ip4);
		EATEST_VERIFY(get_pointer(ip2) == get_pointer(ip4));

		swap(ip1, ip2);
		EATEST_VERIFY(get_pointer(ip1) != NULL);
		EATEST_VERIFY(get_pointer(ip2) == NULL);
		EATEST_VERIFY(get_pointer(ip1) == get_pointer(ip4));
		EATEST_VERIFY(get_pointer(ip2) == get_pointer(ip3));
	}

	{   // Misc tests.
		intrusive_ptr<Test> ip;
		EATEST_VERIFY(ip.get() == NULL);

		ip.reset();
		EATEST_VERIFY(ip.get() == NULL);

		intrusive_ptr<Test> ip2(NULL, false);
		EATEST_VERIFY(ip.get() == NULL);

		bool boolValue = false;
		Test* pTest = new Test(&boolValue);
		EATEST_VERIFY(boolValue);
		pTest->AddRef();
		intrusive_ptr<Test> ip3(pTest, false);
		EATEST_VERIFY(ip3.get() == pTest);
		ip3.reset();
		EATEST_VERIFY(!boolValue);
	}

	{   // Misc tests.
		bool boolArray[3];
		memset(boolArray, 0, sizeof(boolArray));

		Test* p1 = new Test(boolArray + 0);
		EATEST_VERIFY(boolArray[0] && !boolArray[1] && !boolArray[2]);
		intrusive_ptr<Test> arc1(p1);
		EATEST_VERIFY(boolArray[0] && !boolArray[1] && !boolArray[2]);

		Test* p2 = new Test(boolArray + 1);
		EATEST_VERIFY(boolArray[0] && boolArray[1] && !boolArray[2]);
		arc1 = p2;
		EATEST_VERIFY(!boolArray[0] && boolArray[1] && !boolArray[2]);

		Test* p3 = new Test(boolArray + 2);
		EATEST_VERIFY(!boolArray[0] && boolArray[1] && boolArray[2]);
		arc1 = p3;
		EATEST_VERIFY(!boolArray[0] && !boolArray[1] && boolArray[2]);
		arc1 = NULL;
		
		EATEST_VERIFY(!boolArray[0] && !boolArray[1] && !boolArray[2]);
	}

	{ // Test intrusive_ptr_add_ref() / intrusive_ptr_release()
		IntrusiveCustom* const pIC = new IntrusiveCustom;

		{
			intrusive_ptr<IntrusiveCustom> bp = intrusive_ptr<IntrusiveCustom>(pIC);
			intrusive_ptr<IntrusiveCustom> ap = bp;
		}
		
		EATEST_VERIFY((IntrusiveCustom::mAddRefCallCount > 0) && (IntrusiveCustom::mReleaseCallCount == IntrusiveCustom::mAddRefCallCount));
	}

	{ // Regression
		intrusive_ptr<IntrusiveChild>  bp = intrusive_ptr<IntrusiveChild>(new IntrusiveChild);
		intrusive_ptr<IntrusiveParent> ap = bp;
	}

	return nErrorCount;
}


struct RandomLifetimeObject : public eastl::safe_object
{
	void DoSomething() const { }
};



static int Test_safe_ptr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount = 0;

	{   // non-const RandomLifetimeObject
		RandomLifetimeObject* pObject = new RandomLifetimeObject;
		eastl::safe_ptr<RandomLifetimeObject> pSafePtr(pObject);

		eastl::safe_ptr<RandomLifetimeObject> pSafePtrCopy1 = pSafePtr;
		eastl::safe_ptr<RandomLifetimeObject> pSafePtrCopy2(pSafePtr);

		pSafePtr->DoSomething();

		eastl::safe_ptr<RandomLifetimeObject>* pSafePtrCopy3 = new eastl::safe_ptr<RandomLifetimeObject>(pSafePtr);
		eastl::safe_ptr<RandomLifetimeObject>* pSafePtrCopy4 = new eastl::safe_ptr<RandomLifetimeObject>(pSafePtr);
		EATEST_VERIFY(pSafePtrCopy3->get() == pObject);
		EATEST_VERIFY(pSafePtrCopy4->get() == pObject);
		delete pSafePtrCopy3;
		delete pSafePtrCopy4;

		delete pSafePtr;

		EATEST_VERIFY(pSafePtrCopy1.get() == NULL);
		EATEST_VERIFY(pSafePtrCopy2.get() == NULL);
	}

	{   // const RandomLifetimeObject
		RandomLifetimeObject* pObject = new RandomLifetimeObject;
		eastl::safe_ptr<const RandomLifetimeObject> pSafePtr(pObject);

		eastl::safe_ptr<const RandomLifetimeObject> pSafePtrCopy1(pSafePtr);
		eastl::safe_ptr<const RandomLifetimeObject> pSafePtrCopy2 = pSafePtr;

		pSafePtr->DoSomething();

		eastl::safe_ptr<const RandomLifetimeObject>* pSafePtrCopy3 = new eastl::safe_ptr<const RandomLifetimeObject>(pSafePtr);
		eastl::safe_ptr<const RandomLifetimeObject>* pSafePtrCopy4 = new eastl::safe_ptr<const RandomLifetimeObject>(pSafePtr);
		EATEST_VERIFY(pSafePtrCopy3->get() == pObject);
		EATEST_VERIFY(pSafePtrCopy4->get() == pObject);
		delete pSafePtrCopy3;
		delete pSafePtrCopy4;

		delete pSafePtr;

		EATEST_VERIFY(pSafePtrCopy1.get() == NULL);
		EATEST_VERIFY(pSafePtrCopy2.get() == NULL);
	}

	return nErrorCount;
}


int TestSmartPtr()
{
	using namespace SmartPtrTest;
	using namespace eastl;

	int nErrorCount = 0;

	nErrorCount += Test_unique_ptr();
	nErrorCount += Test_scoped_ptr();
	nErrorCount += Test_scoped_array();
	nErrorCount += Test_shared_ptr();
	nErrorCount += Test_shared_ptr_thread();
	nErrorCount += Test_weak_ptr();
	nErrorCount += Test_shared_array();
	nErrorCount += Test_linked_ptr();
	nErrorCount += Test_linked_array();
	nErrorCount += Test_intrusive_ptr();
	nErrorCount += Test_safe_ptr();

	EATEST_VERIFY(A::mCount == 0);
	EATEST_VERIFY(RefCountTest::mCount == 0);
	EATEST_VERIFY(NamedClass::mnCount == 0);
	EATEST_VERIFY(Y::mnCount == 0);
	EATEST_VERIFY(ACLS::mnCount == 0);
	EATEST_VERIFY(BCLS::mnCount == 0);
	EATEST_VERIFY(A1::mnCount == 0);
	EATEST_VERIFY(B1::mnCount == 0);

	return nErrorCount;
}

EA_RESTORE_VC_WARNING()  // 4702







