///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #ifndef EASTLTEST_H #define EASTLTEST_H #include #include EA_DISABLE_ALL_VC_WARNINGS() #include #include #include #include // For the STD_STL_TYPE defines below. #if EASTL_EXCEPTIONS_ENABLED #include #include #endif EA_RESTORE_ALL_VC_WARNINGS(); int TestAlgorithm(); int TestAllocator(); int TestAny(); int TestArray(); int TestBitVector(); int TestBitset(); int TestCharTraits(); int TestChrono(); int TestCppCXTypeTraits(); int TestDeque(); int TestExtra(); int TestFinally(); int TestFixedFunction(); int TestFixedHash(); int TestFixedList(); int TestFixedMap(); int TestFixedSList(); int TestFixedSet(); int TestFixedString(); int TestFixedTupleVector(); int TestFixedVector(); int TestFunctional(); int TestHash(); int TestHeap(); int TestIntrusiveHash(); int TestIntrusiveList(); int TestIntrusiveSDList(); int TestIntrusiveSList(); int TestIterator(); int TestList(); int TestListMap(); int TestLruCache(); int TestMap(); int TestMemory(); int TestMeta(); int TestNumericLimits(); int TestOptional(); int TestRandom(); int TestRatio(); int TestRingBuffer(); int TestSList(); int TestSegmentedVector(); int TestSet(); int TestSmartPtr(); int TestSort(); int TestSpan(); int TestString(); int TestStringHashMap(); int TestStringMap(); int TestStringView(); int TestTuple(); int TestTupleVector(); int TestTypeTraits(); int TestUtility(); int TestVariant(); int TestVector(); int TestVectorMap(); int TestVectorSet(); int TestAtomicBasic(); int TestAtomicAsm(); // Now enable warnings as desired. #ifdef _MSC_VER #pragma warning(disable: 4324) // 'struct_name' : structure was padded due to __declspec(align()) //#pragma warning(disable: 4512) // 'class' : assignment operator could not be generated //#pragma warning(disable: 4100) // 'identifier' : unreferenced formal parameter //#pragma warning(disable: 4706) // assignment within conditional expression #pragma warning(default: 4056) // Floating-point constant arithmetic generates a result that exceeds the maximum allowable value #pragma warning(default: 4061) // The enumerate has no associated handler in a switch statement #pragma warning(default: 4062) // The enumerate has no associated handler in a switch statement, and there is no default label #pragma warning(default: 4191) // Calling this function through the result pointer may cause your program to crash #pragma warning(default: 4217) // Member template functions cannot be used for copy-assignment or copy-construction //#pragma warning(default: 4242) // 'variable' : conversion from 'type' to 'type', possible loss of data #pragma warning(default: 4254) // 'operator' : conversion from 'type1' to 'type2', possible loss of data #pragma warning(default: 4255) // 'function' : no function prototype given: converting '()' to '(void)' #pragma warning(default: 4263) // 'function' : member function does not override any base class virtual member function #pragma warning(default: 4264) // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden #pragma warning(default: 4287) // 'operator' : unsigned/negative constant mismatch #pragma warning(default: 4289) // Nonstandard extension used : 'var' : loop control variable declared in the for-loop is used outside the for-loop scope #pragma warning(default: 4296) // 'operator' : expression is always false #pragma warning(default: 4302) // 'conversion' : truncation from 'type 1' to 'type 2' #pragma warning(default: 4339) // 'type' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception #pragma warning(default: 4347) // Behavior change: 'function template' is called instead of 'function' //#pragma warning(default: 4514) // unreferenced inline/local function has been removed #pragma warning(default: 4529) // 'member_name' : forming a pointer-to-member requires explicit use of the address-of operator ('&') and a qualified name #pragma warning(default: 4545) // Expression before comma evaluates to a function which is missing an argument list #pragma warning(default: 4546) // Function call before comma missing argument list #pragma warning(default: 4547) // 'operator' : operator before comma has no effect; expected operator with side-effect //#pragma warning(default: 4548) // expression before comma has no effect; expected expression with side-effect #pragma warning(default: 4549) // 'operator' : operator before comma has no effect; did you intend 'operator'? #pragma warning(default: 4536) // 'type name' : type-name exceeds meta-data limit of 'limit' characters #pragma warning(default: 4555) // Expression has no effect; expected expression with side-effect #pragma warning(default: 4557) // '__assume' contains effect 'effect' //#pragma warning(default: 4619) // #pragma warning : there is no warning number 'number' #pragma warning(default: 4623) // 'derived class' : default constructor could not be generated because a base class default constructor is inaccessible //#pragma warning(default: 4625) // 'derived class' : copy constructor could not be generated because a base class copy constructor is inaccessible //#pragma warning(default: 4626) // 'derived class' : assignment operator could not be generated because a base class assignment operator is inaccessible #pragma warning(default: 4628) // Digraphs not supported with -Ze. Character sequence 'digraph' not interpreted as alternate token for 'char' #pragma warning(default: 4640) // 'instance' : construction of local static object is not thread-safe #pragma warning(default: 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' #pragma warning(default: 4682) // 'parameter' : no directional parameter attribute specified, defaulting to [in] #pragma warning(default: 4686) // 'user-defined type' : possible change in behavior, change in UDT return calling convention //#pragma warning(default: 4710) // 'function' : function not inlined //#pragma warning(default: 4786) // 'identifier' : identifier was truncated to 'number' characters in the debug information #pragma warning(default: 4793) // Native code generated for function 'function': 'reason' //#pragma warning(default: 4820) // 'bytes' bytes padding added after member 'member' #pragma warning(default: 4905) // Wide string literal cast to 'LPSTR' #pragma warning(default: 4906) // String literal cast to 'LPWSTR' #pragma warning(default: 4917) // 'declarator' : a GUID cannot only be associated with a class, interface or namespace #pragma warning(default: 4928) // Illegal copy-initialization; more than one user-defined conversion has been implicitly applied #pragma warning(default: 4931) // We are assuming the type library was built for number-bit pointers #pragma warning(default: 4946) // reinterpret_cast used between related classes: 'class1' and 'class2' #endif /////////////////////////////////////////////////////////////////////////////// // EASTL includes // // Intentionally keep these includes below the warning settings specified above. // #include #include /// EASTL_TestLevel /// /// Defines how extensive our testing is. A low level is for a desktop or /// nightly build in which the test can run quickly but still hit the /// majority of functionality. High level is for heavy testing and internal /// validation which may take numerous hours to run. /// enum EASTL_TestLevel { kEASTL_TestLevelLow = 1, /// ~10 seconds for test completion. kEASTL_TestLevelHigh = 10 /// Numerous hours for test completion. }; extern int gEASTL_TestLevel; /// EASTLTest_CheckMemory /// /// Does a global memory heap validation check. Returns 0 if OK and /// an error count if there is a problem. /// /// Example usage: /// EASTLTest_CheckMemory(); /// int EASTLTest_CheckMemory_Imp(const char* pFile, int nLine); #define EASTLTest_CheckMemory() EASTLTest_CheckMemory_Imp(__FILE__, __LINE__) // EASTLTEST_STD_STL_VER // #if defined(_STLPORT_VERSION) #define EASTLTEST_STD_STL_VER_STLPORT #elif defined(_RWSTD_VER_STR) || defined(_RWSTD_NAMESPACE_END) #define EASTLTEST_STD_STL_VER_APACHE #elif defined(_CPPLIB_VER) #define EASTLTEST_STD_STL_VER_DINKUMWARE #elif defined(__GNUC__) && defined(_CXXCONFIG) #define EASTLTEST_STD_STL_VER_GCC #else #define EASTLTEST_STD_STL_VER_UNKNOWN #endif /// StdSTLType /// enum StdSTLType { kSTLUnknown, // Unknown type kSTLPort, // STLPort. Descendent of the old HP / SGI STL. kSTLApache, // Apache stdcxx (previously RogueWave), which is a descendent of the old HP / SGI STL. kSTLClang, // Clang native. a.k.a. libc++ kSTLGCC, // GCC native. a.k.a. libstdc++ kSTLMS, // Microsoft. Tweaked version of Dinkumware. kSTLDinkumware // Generic Dinkumware }; StdSTLType GetStdSTLType(); /// GetStdSTLName /// /// Returns the name of the std C++ STL available to the current build. /// The returned value will be one of: /// "STLPort" /// "GCC" /// "VC++" // "Apache" // Previously RogueWave /// const char* GetStdSTLName(); /// gEASTLTest_AllocationCount /// extern int gEASTLTest_AllocationCount; extern int gEASTLTest_TotalAllocationCount; // For backwards compatibility: #define EASTLTest_Printf EA::UnitTest::Report #define VERIFY EATEST_VERIFY /////////////////////////////////////////////////////////////////////////////// /// EASTLTest_Rand /// /// Implements a basic random number generator for EASTL unit tests. It's not /// intended to be a robust random number generator (though it is decent), /// but rather is present so the unit tests can have a portable random number /// generator they can rely on being present. /// /// Example usage: /// EASTLTest_Rand rng; /// eastl_size_t x = rng(); // Generate value in range of [0, 0xffffffff] (i.e. generate any uint32_t) /// eastl_ssize_t y = rng.Rand(1000); // Generate value in range of [0, 1000) /// eastl_ssize_t z = rng.RandRange(-50, +30); // Generate value in range of [-50, +30) /// /// Example usage in the random_shuffle algorithm: /// EASTLTest_Rand rng; /// random_shuffle(first, last, rnd); /// class EASTLTest_Rand { public: EASTLTest_Rand(eastl_size_t nSeed) // The user must supply a seed; we don't provide default values. : mnSeed(nSeed) { } eastl_size_t Rand() { // This is not designed to be a high quality random number generator. if(mnSeed == 0) mnSeed = UINT64_C(0xfefefefefefefefe); // Can't have a seed of zero. const uint64_t nResult64A = ((mnSeed * UINT64_C(6364136223846793005)) + UINT64_C(1442695040888963407)); const uint64_t nResult64B = ((nResult64A * UINT64_C(6364136223846793005)) + UINT64_C(1442695040888963407)); mnSeed = (nResult64A >> 32) ^ nResult64B; return (eastl_size_t)mnSeed; // For eastl_size_t == uint32_t, this is a chop. } eastl_size_t operator()() // Returns a pseudorandom value in range of [0, 0xffffffffffffffff)] (i.e. generate any eastl_size_t) { return Rand(); } eastl_size_t operator()(eastl_size_t n) // Returns a pseudorandom value in range of [0, n) { return RandLimit(n); } eastl_size_t RandLimit(eastl_size_t nLimit) // Returns a pseudorandom value in range of [0, nLimit) { // Can't do the following correct solution because we don't have a portable int128_t to work with. // We could implement a 128 bit multiply manually. See EAStdC/int128_t.cpp. // return (eastl_size_t)((Rand() * (uint128_t)nLimit) >> 64); return (Rand() % nLimit); // This results in an imperfect distribution, especially for the case of nLimit being high relative to eastl_size_t. } eastl_ssize_t RandRange(eastl_ssize_t nBegin, eastl_ssize_t nEnd) // Returns a pseudorandom value in range of [nBegin, nEnd) { return nBegin + (eastl_ssize_t)RandLimit((eastl_size_t)(nEnd - nBegin)); } protected: uint64_t mnSeed; }; /////////////////////////////////////////////////////////////////////////////// /// RandGenT /// /// A wrapper for EASTLTest_Rand which generates values of the given integral /// data type. This is mostly useful for clearnly avoiding compiler warnings, /// as we intentionally enable the highest warning levels in these tests. /// template struct RandGenT { RandGenT(eastl_size_t nSeed) : mRand(nSeed) { } Integer operator()() { return (Integer)mRand.Rand(); } Integer operator()(eastl_size_t n) { return (Integer)mRand.RandLimit(n); } EASTLTest_Rand mRand; }; /////////////////////////////////////////////////////////////////////////////// /// kMagicValue /// /// Used as a unique integer. We assign this to TestObject in its constructor /// and verify in the TestObject destructor that the value is unchanged. /// This can be used to tell, for example, if an invalid object is being /// destroyed. /// const uint32_t kMagicValue = 0x01f1cbe8; /////////////////////////////////////////////////////////////////////////////// /// TestObject /// /// Implements a generic object that is suitable for use in container tests. /// Note that we choose a very restricted set of functions that are available /// for this class. Do not add any additional functions, as that would /// compromise the intentions of the unit tests. /// struct TestObject { int mX; // Value for the TestObject. bool mbThrowOnCopy; // Throw an exception of this object is copied, moved, or assigned to another. int64_t mId; // Unique id for each object, equal to its creation number. This value is not coped from other TestObjects during any operations, including moves. uint32_t mMagicValue; // Used to verify that an instance is valid and that it is not corrupted. It should always be kMagicValue. static int64_t sTOCount; // Count of all current existing TestObjects. static int64_t sTOCtorCount; // Count of times any ctor was called. static int64_t sTODtorCount; // Count of times dtor was called. static int64_t sTODefaultCtorCount; // Count of times the default ctor was called. static int64_t sTOArgCtorCount; // Count of times the x0,x1,x2 ctor was called. static int64_t sTOCopyCtorCount; // Count of times copy ctor was called. static int64_t sTOMoveCtorCount; // Count of times move ctor was called. static int64_t sTOCopyAssignCount; // Count of times copy assignment was called. static int64_t sTOMoveAssignCount; // Count of times move assignment was called. static int sMagicErrorCount; // Number of magic number mismatch errors. explicit TestObject(int x = 0, bool bThrowOnCopy = false) : mX(x), mbThrowOnCopy(bThrowOnCopy), mMagicValue(kMagicValue) { ++sTOCount; ++sTOCtorCount; ++sTODefaultCtorCount; mId = sTOCtorCount; } // This constructor exists for the purpose of testing variadiac template arguments, such as with the emplace container functions. TestObject(int x0, int x1, int x2, bool bThrowOnCopy = false) : mX(x0 + x1 + x2), mbThrowOnCopy(bThrowOnCopy), mMagicValue(kMagicValue) { ++sTOCount; ++sTOCtorCount; ++sTOArgCtorCount; mId = sTOCtorCount; } TestObject(const TestObject& testObject) : mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue) { ++sTOCount; ++sTOCtorCount; ++sTOCopyCtorCount; mId = sTOCtorCount; if(mbThrowOnCopy) { #if EASTL_EXCEPTIONS_ENABLED throw "Disallowed TestObject copy"; #endif } } // Due to the nature of TestObject, there isn't much special for us to // do in our move constructor. A move constructor swaps its contents with // the other object, whhich is often a default-constructed object. TestObject(TestObject&& testObject) : mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue) { ++sTOCount; ++sTOCtorCount; ++sTOMoveCtorCount; mId = sTOCtorCount; // testObject keeps its mId, and we assign ours anew. testObject.mX = 0; // We are swapping our contents with the TestObject, so give it our "previous" value. if(mbThrowOnCopy) { #if EASTL_EXCEPTIONS_ENABLED throw "Disallowed TestObject copy"; #endif } } TestObject& operator=(const TestObject& testObject) { ++sTOCopyAssignCount; if(&testObject != this) { mX = testObject.mX; // Leave mId alone. mMagicValue = testObject.mMagicValue; mbThrowOnCopy = testObject.mbThrowOnCopy; if(mbThrowOnCopy) { #if EASTL_EXCEPTIONS_ENABLED throw "Disallowed TestObject copy"; #endif } } return *this; } TestObject& operator=(TestObject&& testObject) { ++sTOMoveAssignCount; if(&testObject != this) { eastl::swap(mX, testObject.mX); // Leave mId alone. eastl::swap(mMagicValue, testObject.mMagicValue); eastl::swap(mbThrowOnCopy, testObject.mbThrowOnCopy); if(mbThrowOnCopy) { #if EASTL_EXCEPTIONS_ENABLED throw "Disallowed TestObject copy"; #endif } } return *this; } ~TestObject() { if(mMagicValue != kMagicValue) ++sMagicErrorCount; mMagicValue = 0; --sTOCount; ++sTODtorCount; } static void Reset() { sTOCount = 0; sTOCtorCount = 0; sTODtorCount = 0; sTODefaultCtorCount = 0; sTOArgCtorCount = 0; sTOCopyCtorCount = 0; sTOMoveCtorCount = 0; sTOCopyAssignCount = 0; sTOMoveAssignCount = 0; sMagicErrorCount = 0; } static bool IsClear() // Returns true if there are no existing TestObjects and the sanity checks related to that test OK. { return (sTOCount == 0) && (sTODtorCount == sTOCtorCount) && (sMagicErrorCount == 0); } }; // Operators // We specifically define only == and <, in order to verify that // our containers and algorithms are not mistakenly expecting other // operators for the contained and manipulated classes. inline bool operator==(const TestObject& t1, const TestObject& t2) { return t1.mX == t2.mX; } inline bool operator<(const TestObject& t1, const TestObject& t2) { return t1.mX < t2.mX; } // TestObject hash // Normally you don't want to put your hash functions in the eastl namespace, as that namespace is owned by EASTL. // However, these are the EASTL unit tests and we can say that they are also owned by EASTL. namespace eastl { template <> struct hash { size_t operator()(const TestObject& a) const { return static_cast(a.mX); } }; } // use_mX // Used for printing TestObject contents via the PrintSequence function, // which is defined below. See the PrintSequence function for documentation. // This function is an analog of the eastl::use_self and use_first functions. // We declare this all in one line because the user should never need to // debug usage of this function. template struct use_mX { int operator()(const T& t) const { return t.mX; } }; /////////////////////////////////////////////////////////////////////////////// // SizedPOD // // Exists for the purpose testing PODs that are larger than built-in types. // template struct SizedPOD { char memory[kSize]; }; /////////////////////////////////////////////////////////////////////////////// /// ConstType /// /// Used to test const type containers (e.g. vector). /// class ConstType { public: ConstType(int value) : mDummy(value) {}; int mDummy; }; /////////////////////////////////////////////////////////////////////////////// /// TestObjectHash /// /// Implements a manually specified hash function for TestObjects. /// struct TestObjectHash { size_t operator()(const TestObject& t) const { return (size_t)t.mX; } }; /////////////////////////////////////////////////////////////////////////////// /// Align16 /// #if defined(EA_PROCESSOR_ARM) #define kEASTLTestAlign16 8 //ARM processors can only align to 8 #else #define kEASTLTestAlign16 16 #endif EA_PREFIX_ALIGN(kEASTLTestAlign16) struct Align16 { explicit Align16(int x = 0) : mX(x) {} int mX; } EA_POSTFIX_ALIGN(kEASTLTestAlign16); inline bool operator==(const Align16& a, const Align16& b) { return (a.mX == b.mX); } inline bool operator<(const Align16& a, const Align16& b) { return (a.mX < b.mX); } /////////////////////////////////////////////////////////////////////////////// /// Align32 /// #if defined(EA_PROCESSOR_ARM) #define kEASTLTestAlign32 8 //ARM processors can only align to 8 #elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 400) // GCC 2.x, 3.x #define kEASTLTestAlign32 16 // Some versions of GCC fail to support any alignment beyond 16. #else #define kEASTLTestAlign32 32 #endif EA_PREFIX_ALIGN(kEASTLTestAlign32) struct Align32 { explicit Align32(int x = 0) : mX(x) {} int mX; } EA_POSTFIX_ALIGN(kEASTLTestAlign32); inline bool operator==(const Align32& a, const Align32& b) { return (a.mX == b.mX); } inline bool operator<(const Align32& a, const Align32& b) { return (a.mX < b.mX); } /////////////////////////////////////////////////////////////////////////////// /// Align64 /// /// Used for testing of alignment. /// #if defined(EA_PROCESSOR_ARM) #define kEASTLTestAlign64 8 #elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 400) // GCC 2.x, 3.x #define kEASTLTestAlign64 16 // Some versions of GCC fail to support any alignment beyond 16. #else #define kEASTLTestAlign64 64 #endif EA_PREFIX_ALIGN(kEASTLTestAlign64) struct Align64 { explicit Align64(int x = 0) : mX(x) {} int mX; } EA_POSTFIX_ALIGN(kEASTLTestAlign64); inline bool operator==(const Align64& a, const Align64& b) { return (a.mX == b.mX); } inline bool operator<(const Align64& a, const Align64& b) { return (a.mX < b.mX); } namespace eastl { template <> struct hash < Align64 > { size_t operator()(const Align64& a) const { return static_cast(a.mX); } }; } /// test_use_self /// /// Intentionally avoiding a dependency on eastl::use_self. /// template struct test_use_self { const T& operator()(const T& x) const { return x; } }; /// GenerateIncrementalIntegers /// /// Used to seed containers with incremental values based on integers. /// /// Example usage: /// vector v(10, 0); /// generate(v.begin(), v.end(), GenerateIncrementalIntegers()); /// // v will now have 0, 1, 2, ... 8, 9. /// /// generate_n(intArray.begin(), 10, GenerateIncrementalIntegers()); /// // v will now have 0, 1, 2, ... 8, 9. /// /// vector vTO(10, 0); /// generate(vTO.begin(), vTO.end(), GenerateIncrementalIntegers()); /// // vTO will now have 0, 1, 2, ... 8, 9. /// template struct GenerateIncrementalIntegers { int mX; GenerateIncrementalIntegers(int x = 0) : mX(x) { } void reset(int x = 0) { mX = x; } T operator()() { return T(mX++); } }; /// SetIncrementalIntegers /// /// Used to seed containers with incremental values based on integers. /// /// Example usage: /// vector v(10, 0); /// for_each(v.begin(), v.end(), SetIncrementalIntegers()); /// // v will now have 0, 1, 2, ... 8, 9. /// template struct SetIncrementalIntegers { int mX; SetIncrementalIntegers(int x = 0) : mX(x) { } void reset(int x = 0) { mX = x; } void operator()(T& t) { t = T(mX++); } }; /// CompareContainers /// /// Does a comparison between the contents of two containers. /// /// Specifically tests for the following properties: /// empty() is the same for both /// size() is the same for both /// iteration through both element by element yields equal values. /// template int CompareContainers(const T1& t1, const T2& t2, const char* ppName, ExtractValue1 ev1 = test_use_self(), ExtractValue2 ev2 = test_use_self()) { int nErrorCount = 0; // Compare emptiness. VERIFY(t1.empty() == t2.empty()); // Compare sizes. const size_t nSize1 = t1.size(); const size_t nSize2 = t2.size(); VERIFY(nSize1 == nSize2); if(nSize1 != nSize2) EASTLTest_Printf("%s: Container size difference: %u, %u\n", ppName, (unsigned)nSize1, (unsigned)nSize2); // Compare values. if(nSize1 == nSize2) { // Test iteration typename T1::const_iterator it1 = t1.begin(); typename T2::const_iterator it2 = t2.begin(); for(unsigned j = 0; it1 != t1.end(); ++it1, ++it2, ++j) { const typename T1::value_type& v1 = *it1; const typename T2::value_type& v2 = *it2; VERIFY(ev1(v1) == ev2(v2)); if(!(ev1(v1) == ev2(v2))) { EASTLTest_Printf("%s: Container iterator difference at index %d\n", ppName, j); break; } } VERIFY(it1 == t1.end()); VERIFY(it2 == t2.end()); } return nErrorCount; } /// VerifySequence /// /// Allows the user to specify that a container has a given set of values. /// /// Example usage: /// vector v; /// v.push_back(1); v.push_back(3); v.push_back(5); /// VerifySequence(v.begin(), v.end(), int(), "v.push_back", 1, 3, 5, -1); /// /// Note: The StackValue template argument is a hint to the compiler about what type /// the passed vararg sequence is. /// template bool VerifySequence(InputIterator first, InputIterator last, StackValue /*unused*/, const char* pName, ...) { typedef typename eastl::iterator_traits::value_type value_type; int argIndex = 0; int seqIndex = 0; bool bReturnValue = true; StackValue next; va_list args; va_start(args, pName); for( ; first != last; ++first, ++argIndex, ++seqIndex) { next = va_arg(args, StackValue); if((next == StackValue(-1)) || !(value_type(next) == *first)) { if(pName) EASTLTest_Printf("[%s] Mismatch at index %d\n", pName, argIndex); else EASTLTest_Printf("Mismatch at index %d\n", argIndex); bReturnValue = false; } } for(; first != last; ++first) ++seqIndex; if(bReturnValue) { next = va_arg(args, StackValue); if(!(next == StackValue(-1))) { do { ++argIndex; next = va_arg(args, StackValue); } while(!(next == StackValue(-1))); if(pName) EASTLTest_Printf("[%s] Too many elements: expected %d, found %d\n", pName, argIndex, seqIndex); else EASTLTest_Printf("Too many elements: expected %d, found %d\n", argIndex, seqIndex); bReturnValue = false; } } va_end(args); return bReturnValue; } /// PrintSequence /// /// Allows the user to print a sequence of values. /// /// Example usage: /// vector v; /// PrintSequence(v.begin(), v.end(), use_self(), 100, "vector", 1, 3, 5, -1); /// /// Example usage: /// template struct use_mX { int operator()(const T& t) const { return t.mX; } }; /// vector v; /// PrintSequence(v.begin(), v.end(), use_mX(), 100, "vector", 1, 3, 5, -1); /// template void PrintSequence(InputIterator first, InputIterator last, ExtractInt extractInt, int nMaxCount, const char* pName, ...) { if(pName) EASTLTest_Printf("[%s]", pName); for(int i = 0; (i < nMaxCount) && (first != last); ++i, ++first) { EASTLTest_Printf("%d ", (int)extractInt(*first)); } EASTLTest_Printf("\n"); } /// demoted_iterator /// /// Converts an iterator into a demoted category. For example, you can convert /// an iterator of type bidirectional_iterator_tag to forward_iterator_tag. /// The following is a list of iterator types. A demonted iterator can be demoted /// only to a lower iterator category (earlier in the following list): /// input_iterator_tag /// forward_iterator_tag /// bidirectional_iterator_tag /// random_access_iterator_tag /// contiguous_iterator_tag /// /// Converts something which can be iterated into a formal input iterator. /// This class is useful for testing functions and algorithms that expect /// InputIterators, which are the lowest and 'weakest' form of iterators. /// /// Key traits of InputIterators: /// Algorithms on input iterators should never attempt to pass /// through the same iterator twice. They should be single pass /// algorithms. value_type T is not required to be an lvalue type. /// /// Example usage: /// typedef demoted_iterator PointerAsBidirectionalIterator; /// typedef demoted_iterator VectorIteratorAsForwardIterator; /// /// Example usage: /// IntVector v; /// comb_sort(to_forward_iterator(v.begin()), to_forward_iterator(v.end())); /// template class demoted_iterator { protected: Iterator mIterator; public: typedef demoted_iterator this_type; typedef Iterator iterator_type; typedef IteratorCategory iterator_category; typedef typename eastl::iterator_traits::value_type value_type; typedef typename eastl::iterator_traits::difference_type difference_type; typedef typename eastl::iterator_traits::reference reference; typedef typename eastl::iterator_traits::pointer pointer; demoted_iterator() : mIterator() { } explicit demoted_iterator(const Iterator& i) : mIterator(i) { } demoted_iterator(const this_type& x) : mIterator(x.mIterator) { } this_type& operator=(const Iterator& i) { mIterator = i; return *this; } this_type& operator=(const this_type& x) { mIterator = x.mIterator; return *this; } reference operator*() const { return *mIterator; } pointer operator->() const { return mIterator; } this_type& operator++() { ++mIterator; return *this; } this_type operator++(int) { return this_type(mIterator++); } this_type& operator--() { --mIterator; return *this; } this_type operator--(int) { return this_type(mIterator--); } reference operator[](const difference_type& n) const { return mIterator[n]; } this_type& operator+=(const difference_type& n) { mIterator += n; return *this; } this_type operator+(const difference_type& n) const { return this_type(mIterator + n); } this_type& operator-=(const difference_type& n) { mIterator -= n; return *this; } this_type operator-(const difference_type& n) const { return this_type(mIterator - n); } const iterator_type& base() const { return mIterator; } }; // class demoted_iterator template inline bool operator==(const demoted_iterator& a, const demoted_iterator& b) { return a.base() == b.base(); } template inline bool operator!=(const demoted_iterator& a, const demoted_iterator& b) { return !(a == b); } template inline bool operator<(const demoted_iterator& a, const demoted_iterator& b) { return a.base() < b.base(); } template inline bool operator<=(const demoted_iterator& a, const demoted_iterator& b) { return !(b < a); } template inline bool operator>(const demoted_iterator& a, const demoted_iterator& b) { return b < a; } template inline bool operator>=(const demoted_iterator& a, const demoted_iterator& b) { return !(a < b); } template inline demoted_iterator operator-(const demoted_iterator& a, const demoted_iterator& b) { return demoted_iterator(a.base() - b.base()); } template inline demoted_iterator operator+(typename demoted_iterator::difference_type n, const demoted_iterator& a) { return a + n; } // to_xxx_iterator // // Returns a demoted iterator // template inline demoted_iterator to_input_iterator(const Iterator& i) { return demoted_iterator(i); } template inline demoted_iterator to_forward_iterator(const Iterator& i) { return demoted_iterator(i); } template inline demoted_iterator to_bidirectional_iterator(const Iterator& i) { return demoted_iterator(i); } template inline demoted_iterator to_random_access_iterator(const Iterator& i) { return demoted_iterator(i); } /////////////////////////////////////////////////////////////////////////////// // MallocAllocator // // Implements an EASTL allocator that uses malloc/free as opposed to // new/delete or PPMalloc Malloc/Free. This is useful for testing // allocator behaviour of code. // // Example usage: // vector intVector; // class MallocAllocator { public: MallocAllocator(const char* = EASTL_NAME_VAL("MallocAllocator")) : mAllocCount(0), mFreeCount(0), mAllocVolume(0) {} MallocAllocator(const MallocAllocator& x) : mAllocCount(x.mAllocCount), mFreeCount(x.mFreeCount), mAllocVolume(x.mAllocVolume) {} MallocAllocator(const MallocAllocator& x, const char*) : MallocAllocator(x) {} MallocAllocator& operator=(const MallocAllocator& x) { mAllocCount = x.mAllocCount; mFreeCount = x.mFreeCount; mAllocVolume = x.mAllocVolume; return *this; } void* allocate(size_t n, int = 0); void* allocate(size_t n, size_t, size_t, int = 0); // We don't support alignment, so you can't use this class where alignment is required. void deallocate(void* p, size_t n); const char* get_name() const { return "MallocAllocator"; } void set_name(const char*) {} static void reset_all() { mAllocCountAll = 0; mFreeCountAll = 0; mAllocVolumeAll = 0; mpLastAllocation = NULL; } public: int mAllocCount; int mFreeCount; size_t mAllocVolume; static int mAllocCountAll; static int mFreeCountAll; static size_t mAllocVolumeAll; static void* mpLastAllocation; }; inline bool operator==(const MallocAllocator&, const MallocAllocator&) { return true; } inline bool operator!=(const MallocAllocator&, const MallocAllocator&) { return false; } /////////////////////////////////////////////////////////////////////////////// // CustomAllocator // // Implements an allocator that works just like eastl::allocator but is defined // within this test as opposed to within EASTL. // // Example usage: // vector intVector; // class CustomAllocator { public: CustomAllocator(const char* = NULL) {} CustomAllocator(const CustomAllocator&) {} CustomAllocator(const CustomAllocator&, const char*) {} CustomAllocator& operator=(const CustomAllocator&) { return *this; } void* allocate(size_t n, int flags = 0); void* allocate(size_t n, size_t, size_t, int flags = 0); void deallocate(void* p, size_t n); const char* get_name() const { return "CustomAllocator"; } void set_name(const char*) {} }; inline bool operator==(const CustomAllocator&, const CustomAllocator&) { return true; } inline bool operator!=(const CustomAllocator&, const CustomAllocator&) { return false; } /////////////////////////////////////////////////////////////////////////////// /// UnequalAllocator /// /// Acts the same as eastl::allocator, but always compares as unequal to an /// instance of itself. /// class UnequalAllocator { public: EASTL_ALLOCATOR_EXPLICIT UnequalAllocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME)) : mAllocator(pName) {} UnequalAllocator(const UnequalAllocator& x) : mAllocator(x.mAllocator) {} UnequalAllocator(const UnequalAllocator& x, const char* pName) : mAllocator(x.mAllocator) { set_name(pName); } UnequalAllocator& operator=(const UnequalAllocator& x) { mAllocator = x.mAllocator; return *this; } void* allocate(size_t n, int flags = 0) { return mAllocator.allocate(n, flags); } void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) { return mAllocator.allocate(n, alignment, offset, flags); } void deallocate(void* p, size_t n) { return mAllocator.deallocate(p, n); } const char* get_name() const { return mAllocator.get_name(); } void set_name(const char* pName) { mAllocator.set_name(pName); } protected: eastl::allocator mAllocator; }; inline bool operator==(const UnequalAllocator&, const UnequalAllocator&) { return false; } inline bool operator!=(const UnequalAllocator&, const UnequalAllocator&) { return true; } /////////////////////////////////////////////////////////////////////////////// /// CountingAllocator /// /// Counts allocation events allowing unit tests to validate assumptions. /// class CountingAllocator : public eastl::allocator { public: using base_type = eastl::allocator; EASTL_ALLOCATOR_EXPLICIT CountingAllocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME)) : base_type(pName) { totalCtorCount++; defaultCtorCount++; } CountingAllocator(const CountingAllocator& x) : base_type(x) { totalCtorCount++; copyCtorCount++; } CountingAllocator(const CountingAllocator& x, const char* pName) : base_type(x) { totalCtorCount++; copyCtorCount++; set_name(pName); } CountingAllocator& operator=(const CountingAllocator& x) { base_type::operator=(x); assignOpCount++; return *this; } virtual void* allocate(size_t n, int flags = 0) { activeAllocCount++; totalAllocCount++; totalAllocatedMemory += n; activeAllocatedMemory += n; return base_type::allocate(n, flags); } virtual void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) { activeAllocCount++; totalAllocCount++; totalAllocatedMemory += n; activeAllocatedMemory += n; return base_type::allocate(n, alignment, offset, flags); } void deallocate(void* p, size_t n) { activeAllocCount--; totalDeallocCount--; activeAllocatedMemory -= n; return base_type::deallocate(p, n); } const char* get_name() const { return base_type::get_name(); } void set_name(const char* pName) { base_type::set_name(pName); } static auto getTotalAllocationCount() { return totalAllocCount; } static auto getTotalAllocationSize() { return totalAllocatedMemory; } static auto getActiveAllocationSize() { return activeAllocatedMemory; } static auto getActiveAllocationCount() { return activeAllocCount; } static auto neverUsed() { return totalAllocCount == 0; } static void resetCount() { activeAllocCount = 0; totalAllocCount = 0; totalDeallocCount = 0; totalCtorCount = 0; defaultCtorCount = 0; copyCtorCount = 0; assignOpCount = 0; totalAllocatedMemory = 0; activeAllocatedMemory = 0; } static uint64_t activeAllocCount; static uint64_t totalAllocCount; static uint64_t totalDeallocCount; static uint64_t totalCtorCount; static uint64_t defaultCtorCount; static uint64_t copyCtorCount; static uint64_t assignOpCount; static uint64_t totalAllocatedMemory; // the total amount of memory allocated static uint64_t activeAllocatedMemory; // currently allocated memory by allocator }; inline bool operator==(const CountingAllocator& rhs, const CountingAllocator& lhs) { return operator==(CountingAllocator::base_type(rhs), CountingAllocator::base_type(lhs)); } inline bool operator!=(const CountingAllocator& rhs, const CountingAllocator& lhs) { return !(rhs == lhs); } /////////////////////////////////////////////////////////////////////////////// // InstanceAllocator // // Implements an allocator which has a instance id that makes it different // from other InstanceAllocators of a different id. Allocations between // InstanceAllocators of different ids are incompatible. An allocation done // by an InstanceAllocator of id=0 cannot be freed by an InstanceAllocator // of id=1. // // Example usage: // InstanceAllocator ia0((uint8_t)0); // InstanceAllocator ia1((uint8_t)1); // // eastl::list list0(1, ia0); // eastl::list list1(1, ia1); // // list0 = list1; // list0 cannot free it's current contents with list1's allocator, and InstanceAllocator's purpose is to detect if it mistakenly does so. // class InstanceAllocator { public: enum { kMultiplier = 16 }; // Use 16 because it's the highest currently known platform alignment requirement. InstanceAllocator(const char* = NULL, uint8_t instanceId = 0) : mInstanceId(instanceId) {} InstanceAllocator(uint8_t instanceId) : mInstanceId(instanceId) {} InstanceAllocator(const InstanceAllocator& x) : mInstanceId(x.mInstanceId) {} InstanceAllocator(const InstanceAllocator& x, const char*) : mInstanceId(x.mInstanceId) {} InstanceAllocator& operator=(const InstanceAllocator& x) { mInstanceId = x.mInstanceId; return *this; } void* allocate(size_t n, int = 0) { // +1 so that we always have space to write mInstanceId. uint8_t* p8 = static_cast(malloc(n + (kMultiplier * (mInstanceId + 1)))); // We make allocations between // different instances incompatible by // tweaking their return values. eastl::fill(p8, p8 + kMultiplier, 0xff); EA_ANALYSIS_ASSUME(p8 != NULL); *p8 = mInstanceId; return p8 + (kMultiplier * (mInstanceId + 1)); } void* allocate(size_t n, size_t, size_t, int = 0) { // +1 so that we always have space to write mInstanceId. uint8_t* p8 = static_cast(malloc(n + (kMultiplier * (mInstanceId + 1)))); // We make allocations between // different instances incompatible by // tweaking their return values. eastl::fill(p8, p8 + kMultiplier, 0xff); EA_ANALYSIS_ASSUME(p8 != NULL); *p8 = mInstanceId; return p8 + (kMultiplier * (mInstanceId + 1)); } void deallocate(void* p, size_t /*n*/) { uint8_t* p8 = static_cast(p) - (kMultiplier * (mInstanceId + 1)); EASTL_ASSERT(*p8 == mInstanceId); // mInstanceId must match the id used in allocate(), otherwise the behavior is // undefined (probably a heap assert). if (*p8 == mInstanceId) // It's possible that *p8 coincidentally matches mInstanceId if p8 is offset into memory // we don't control. free(p8); else ++mMismatchCount; } const char* get_name() { sprintf(mName, "InstanceAllocator %u", mInstanceId); return mName; } void set_name(const char*) {} static void reset_all() { mMismatchCount = 0; } public: uint8_t mInstanceId; char mName[32]; static int mMismatchCount; }; inline bool operator==(const InstanceAllocator& a, const InstanceAllocator& b) { return (a.mInstanceId == b.mInstanceId); } inline bool operator!=(const InstanceAllocator& a, const InstanceAllocator& b) { return (a.mInstanceId != b.mInstanceId); } /////////////////////////////////////////////////////////////////////////////// // ThrowingAllocator // // Implements an EASTL allocator that uses malloc/free as opposed to // new/delete or PPMalloc Malloc/Free. This is useful for testing // allocator behaviour of code. // // Example usage: // vector > intVector; // template class ThrowingAllocator { public: ThrowingAllocator(const char* = EASTL_NAME_VAL("ThrowingAllocator")) : mbShouldThrow(initialShouldThrow) {} ThrowingAllocator(const ThrowingAllocator& x) : mbShouldThrow(x.mbShouldThrow) {} ThrowingAllocator(const ThrowingAllocator& x, const char*) : mbShouldThrow(x.mbShouldThrow) {} ThrowingAllocator& operator=(const ThrowingAllocator& x) { mbShouldThrow = x.mbShouldThrow; return *this; } void* allocate(size_t n, int = 0) { #if EASTL_EXCEPTIONS_ENABLED if (mbShouldThrow) throw std::bad_alloc(); #endif return malloc(n); } void* allocate(size_t n, size_t, size_t, int = 0) { #if EASTL_EXCEPTIONS_ENABLED if (mbShouldThrow) throw std::bad_alloc(); #endif return malloc(n); // We don't support alignment, so you can't use this class where alignment is required. } void deallocate(void* p, size_t) { free(p); } const char* get_name() const { return "ThrowingAllocator"; } void set_name(const char*) {} void set_should_throw(bool shouldThrow) { mbShouldThrow = shouldThrow; } bool get_should_throw() const { return mbShouldThrow; } protected: bool mbShouldThrow; }; template inline bool operator==(const ThrowingAllocator&, const ThrowingAllocator&) { return true; } template inline bool operator!=(const ThrowingAllocator&, const ThrowingAllocator&) { return false; } /////////////////////////////////////////////////////////////////////////////// // Helper utility that does a case insensitive string comparsion with two sets of overloads // struct TestStrCmpI_2 { bool operator()(const char* pCStr, const eastl::string& str) const { return str.comparei(pCStr) == 0; } bool operator()(const eastl::string& str, const char* pCStr) const { return str.comparei(pCStr) == 0; } }; /////////////////////////////////////////////////////////////////////////////// // StompDetectAllocator // // An allocator that has sentinal values surrounding its allocator in an // effort to detected if its internal memory has been stomped. // static uint64_t STOMP_MAGIC_V1 = 0x0101DEC1A551F1ED; static uint64_t STOMP_MAGIC_V2 = 0x12345C1A551F1ED5; struct StompDetectAllocator { StompDetectAllocator() { Validate(); } ~StompDetectAllocator() { Validate(); } StompDetectAllocator(const char*) { Validate(); } void* allocate(size_t n, int = 0) { return mMallocAllocator.allocate(n); } void* allocate(size_t n, size_t, size_t, int = 0) { return mMallocAllocator.allocate(n); } void deallocate(void* p, size_t n) { mMallocAllocator.deallocate(p, n); } const char* get_name() const { return "FatAllocator"; } void set_name(const char*) {} void Validate() const { EASTL_ASSERT(mSentinal1 == STOMP_MAGIC_V1); EASTL_ASSERT(mSentinal2 == STOMP_MAGIC_V2); } uint64_t mSentinal1 = STOMP_MAGIC_V1; MallocAllocator mMallocAllocator; uint64_t mSentinal2 = STOMP_MAGIC_V2; }; inline bool operator==(const StompDetectAllocator& a, const StompDetectAllocator& b) { a.Validate(); b.Validate(); return (a.mMallocAllocator == b.mMallocAllocator); } inline bool operator!=(const StompDetectAllocator& a, const StompDetectAllocator& b) { a.Validate(); b.Validate(); return (a.mMallocAllocator != b.mMallocAllocator); } // Commonly used free-standing functions to test callables inline int ReturnVal(int param) { return param; } inline int ReturnZero() { return 0; } inline int ReturnOne() { return 1; } // ValueInit template struct ValueInitOf { ValueInitOf() : mV() {} ~ValueInitOf() = default; ValueInitOf(const ValueInitOf&) = default; ValueInitOf(ValueInitOf&&) = default; ValueInitOf& operator=(const ValueInitOf&) = default; ValueInitOf& operator=(ValueInitOf&&) = default; T get() { return mV; } T mV; }; // MoveOnlyType - useful for verifying containers that may hold, e.g., unique_ptrs to make sure move ops are implemented struct MoveOnlyType { MoveOnlyType() = delete; MoveOnlyType(int val) : mVal(val) {} MoveOnlyType(const MoveOnlyType&) = delete; MoveOnlyType(MoveOnlyType&& x) : mVal(x.mVal) { x.mVal = 0; } MoveOnlyType& operator=(const MoveOnlyType&) = delete; MoveOnlyType& operator=(MoveOnlyType&& x) { mVal = x.mVal; x.mVal = 0; return *this; } bool operator==(const MoveOnlyType& o) const { return mVal == o.mVal; } int mVal; }; // MoveOnlyTypeDefaultCtor - useful for verifying containers that may hold, e.g., unique_ptrs to make sure move ops are implemented struct MoveOnlyTypeDefaultCtor { MoveOnlyTypeDefaultCtor() = default; MoveOnlyTypeDefaultCtor(int val) : mVal(val) {} MoveOnlyTypeDefaultCtor(const MoveOnlyTypeDefaultCtor&) = delete; MoveOnlyTypeDefaultCtor(MoveOnlyTypeDefaultCtor&& x) : mVal(x.mVal) { x.mVal = 0; } MoveOnlyTypeDefaultCtor& operator=(const MoveOnlyTypeDefaultCtor&) = delete; MoveOnlyTypeDefaultCtor& operator=(MoveOnlyTypeDefaultCtor&& x) { mVal = x.mVal; x.mVal = 0; return *this; } bool operator==(const MoveOnlyTypeDefaultCtor& o) const { return mVal == o.mVal; } int mVal; }; ////////////////////////////////////////////////////////////////////////////// // Utility RAII class that sets a new default allocator for the scope // struct AutoDefaultAllocator { eastl::allocator* mPrevAllocator = nullptr; AutoDefaultAllocator(eastl::allocator* nextAllocator) { mPrevAllocator = SetDefaultAllocator(nextAllocator); } ~AutoDefaultAllocator() { SetDefaultAllocator(mPrevAllocator); } }; #endif // Header include guard