aboutsummaryrefslogtreecommitdiff
path: root/EASTL/test/source/TestAny.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'EASTL/test/source/TestAny.cpp')
-rw-r--r--EASTL/test/source/TestAny.cpp472
1 files changed, 472 insertions, 0 deletions
diff --git a/EASTL/test/source/TestAny.cpp b/EASTL/test/source/TestAny.cpp
new file mode 100644
index 0000000..fedc85f
--- /dev/null
+++ b/EASTL/test/source/TestAny.cpp
@@ -0,0 +1,472 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#include "EASTLTest.h"
+#include <EASTL/any.h>
+#include <EASTL/vector.h>
+#include <EASTL/string.h>
+#include <EASTL/numeric.h>
+#include <EAStdC/EAString.h>
+
+
+// SmallTestObject
+//
+struct SmallTestObject
+{
+ static int mCtorCount;
+
+ SmallTestObject() EA_NOEXCEPT { mCtorCount++; }
+ SmallTestObject(const SmallTestObject&) EA_NOEXCEPT { mCtorCount++; }
+ SmallTestObject(SmallTestObject&&) EA_NOEXCEPT { mCtorCount++; }
+ SmallTestObject& operator=(const SmallTestObject&) EA_NOEXCEPT { mCtorCount++; return *this; }
+ ~SmallTestObject() EA_NOEXCEPT { mCtorCount--; }
+
+ static void Reset() { mCtorCount = 0; }
+ static bool IsClear() { return mCtorCount == 0; }
+};
+
+int SmallTestObject::mCtorCount = 0;
+
+
+// RequiresInitList
+//
+struct RequiresInitList
+{
+ RequiresInitList(std::initializer_list<int> ilist)
+ : sum(eastl::accumulate(begin(ilist), end(ilist), 0)) {}
+
+ int sum;
+};
+
+
+int TestAny()
+{
+ using namespace eastl;
+ int nErrorCount = 0;
+
+ // NOTE(rparolin): Ensure 'any' is at least the size of an eastl::string and an eastl::vector to prevent heap
+ // allocation of handle objects (objects that point to a heap allocation). This will reduce memory pressure since
+ // eastl::string will be a commonly used type. We could also test with a vector.
+ {
+ static_assert(sizeof(eastl::string) <= sizeof(eastl::any), "ensure that 'any' has enough local memory to store a string");
+ static_assert(sizeof(eastl::vector<int>) <= sizeof(eastl::any), "ensure that 'any' has enough local memory to store a vector");
+ }
+
+ {
+ // default construct
+ any a;
+ VERIFY(a.has_value() == false);
+ }
+
+ {
+ // test object ctors & dtors are called for a large object
+ TestObject::Reset();
+ { any a{TestObject()}; }
+ VERIFY(TestObject::IsClear());
+ }
+
+ {
+ // test object ctors & dtors are called for a small object
+ SmallTestObject::Reset();
+ { any a{SmallTestObject()}; }
+ VERIFY(SmallTestObject::IsClear());
+ }
+
+ {
+ any a(42);
+ VERIFY(a.has_value() == true);
+
+ VERIFY(any_cast<int>(a) == 42);
+ VERIFY(any_cast<int>(a) != 1337);
+ any_cast<int&>(a) = 10;
+ VERIFY(any_cast<int>(a) == 10);
+
+ a = 1.f;
+ any_cast<float&>(a) = 1337.f;
+ VERIFY(any_cast<float>(a) == 1337.f);
+
+ a = 4343;
+ VERIFY(any_cast<int>(a) == 4343);
+
+ a = string("hello world");
+ VERIFY(any_cast<string>(a) == "hello world");
+ VERIFY(any_cast<string&>(a) == "hello world");
+ }
+
+ {
+ struct custom_type { int data; };
+
+ any a = custom_type{};
+ any_cast<custom_type&>(a).data = 42;
+ VERIFY(any_cast<custom_type>(a).data == 42);
+ }
+
+ {
+ any a = 42;
+ VERIFY(any_cast<int>(a) == 42);
+
+ #if EASTL_EXCEPTIONS_ENABLED
+ int throwCount = 0;
+ try { VERIFY(any_cast<short>(a) == 42); }
+ catch (bad_any_cast) { throwCount++; }
+ VERIFY(throwCount != 0);
+ #endif
+ }
+
+ {
+ vector<any> va = {42, 'a', 42.f, 3333u, 4444ul, 5555ull, 6666.0};
+
+ VERIFY(any_cast<int>(va[0]) == 42);
+ VERIFY(any_cast<char>(va[1]) == 'a');
+ VERIFY(any_cast<float>(va[2]) == 42.f);
+ VERIFY(any_cast<unsigned>(va[3]) == 3333u);
+ VERIFY(any_cast<unsigned long>(va[4]) == 4444ul);
+ VERIFY(any_cast<unsigned long long>(va[5]) == 5555ull);
+ VERIFY(any_cast<double>(va[6]) == 6666.0);
+ }
+
+ {
+ any a(string("test string"));
+ VERIFY(a.has_value());
+ VERIFY(any_cast<string>(a) == "test string");
+ }
+
+ {
+ vector<any> va = {42, string("rob"), 'a', 42.f};
+ VERIFY(any_cast<int>(va[0]) == 42);
+ VERIFY(any_cast<string>(va[1]) == "rob");
+ VERIFY(any_cast<char>(va[2]) == 'a');
+ VERIFY(any_cast<float>(va[3]) == 42.f);
+ }
+
+ {
+ vector<any> va;
+ va.push_back(42);
+ va.push_back(string("rob"));
+ va.push_back('a');
+ va.push_back(42.f);
+
+ VERIFY(any_cast<int>(va[0]) == 42);
+ VERIFY(any_cast<string>(va[1]) == "rob");
+ VERIFY(any_cast<char>(va[2]) == 'a');
+ VERIFY(any_cast<float>(va[3]) == 42.f);
+ }
+
+ // NOTE(rparolin): Replaces a small 'any' object with a large one and make sure it doesn't corrupt
+ // the surrounding memory in the vector.
+ {
+ TestObject::Reset();
+ {
+ vector<any> va = {42, 'a', 42.f, 3333u, 4444ul, 5555ull, 6666.0};
+
+ VERIFY(any_cast<int>(va[0]) == 42);
+ VERIFY(any_cast<char>(va[1]) == 'a');
+ VERIFY(any_cast<float>(va[2]) == 42.f);
+ VERIFY(any_cast<unsigned>(va[3]) == 3333u);
+ VERIFY(any_cast<unsigned long>(va[4]) == 4444ul);
+ VERIFY(any_cast<unsigned long long>(va[5]) == 5555ull);
+ VERIFY(any_cast<double>(va[6]) == 6666.0);
+
+ va[3] = TestObject(3333); // replace a small integral with a large heap allocated object.
+
+ VERIFY(any_cast<int>(va[0]) == 42);
+ VERIFY(any_cast<char>(va[1]) == 'a');
+ VERIFY(any_cast<float>(va[2]) == 42.f);
+ VERIFY(any_cast<TestObject>(va[3]).mX == 3333); // not 3333u because TestObject ctor takes a signed type.
+ VERIFY(any_cast<unsigned long>(va[4]) == 4444ul);
+ VERIFY(any_cast<unsigned long long>(va[5]) == 5555ull);
+ VERIFY(any_cast<double>(va[6]) == 6666.0);
+ }
+ VERIFY(TestObject::IsClear());
+ }
+
+ {
+ any a(string("test string"));
+ VERIFY(a.has_value());
+ a.reset();
+ VERIFY(!a.has_value());
+ }
+
+ {
+ any a1 = 42;
+ any a2 = a1;
+
+ VERIFY(a1.has_value());
+ VERIFY(a2.has_value());
+ VERIFY(any_cast<int>(a1) == any_cast<int>(a2));
+ }
+
+ {
+ any a1;
+ VERIFY(!a1.has_value());
+ {
+ any a2(string("test string"));
+ a1 = any_cast<string>(a2);
+
+ VERIFY(a1.has_value());
+ }
+ VERIFY(any_cast<string>(a1) == "test string");
+ VERIFY(a1.has_value());
+ }
+
+ {
+ any a1;
+ VERIFY(!a1.has_value());
+ {
+ any a2(string("test string"));
+ a1 = a2;
+ VERIFY(a1.has_value());
+ }
+ VERIFY(any_cast<string&>(a1) == "test string");
+ VERIFY(a1.has_value());
+ }
+
+ // swap tests
+ {
+ {
+ any a1 = 42;
+ any a2 = 24;
+ VERIFY(any_cast<int>(a1) == 42);
+ VERIFY(any_cast<int>(a2) == 24);
+
+ a1.swap(a2);
+ VERIFY(any_cast<int>(a1) == 24);
+ VERIFY(any_cast<int>(a2) == 42);
+
+ eastl::swap(a1, a2);
+ VERIFY(any_cast<int>(a1) == 42);
+ VERIFY(any_cast<int>(a2) == 24);
+ }
+ {
+ any a1 = string("hello");
+ any a2 = string("world");
+ VERIFY(any_cast<string>(a1) == "hello");
+ VERIFY(any_cast<string>(a2) == "world");
+
+ a1.swap(a2);
+ VERIFY(any_cast<string>(a1) == "world");
+ VERIFY(any_cast<string>(a2) == "hello");
+
+ eastl::swap(a1, a2);
+ VERIFY(any_cast<string>(a1) == "hello");
+ VERIFY(any_cast<string>(a2) == "world");
+ }
+ }
+
+ #if EASTL_RTTI_ENABLED
+ {
+ #if defined(EA_COMPILER_MSVC)
+ VERIFY(EA::StdC::Strcmp(any(42).type().name(), "int") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42.f).type().name(), "float") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42u).type().name(), "unsigned int") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42ul).type().name(), "unsigned long") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42l).type().name(), "long") == 0);
+
+ #elif defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)
+ VERIFY(EA::StdC::Strcmp(any(42).type().name(), "i") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42.f).type().name(), "f") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42u).type().name(), "j") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42ul).type().name(), "m") == 0);
+ VERIFY(EA::StdC::Strcmp(any(42l).type().name(), "l") == 0);
+ #endif
+ }
+ #endif
+
+ // emplace, small object tests
+ {
+ any a;
+
+ a.emplace<int>(42);
+ VERIFY(a.has_value());
+ VERIFY(any_cast<int>(a) == 42);
+
+ a.emplace<short>((short)8); // no way to define a short literal we must cast here.
+ VERIFY(any_cast<short>(a) == 8);
+ VERIFY(a.has_value());
+
+ a.reset();
+ VERIFY(!a.has_value());
+ }
+
+ // emplace, large object tests
+ {
+ TestObject::Reset();
+ {
+ any a;
+ a.emplace<TestObject>();
+ VERIFY(a.has_value());
+ }
+ VERIFY(TestObject::IsClear());
+ }
+
+ // emplace, initializer_list
+ {
+ {
+ any a;
+ a.emplace<RequiresInitList>(std::initializer_list<int>{1,2,3,4,5,6});
+
+ VERIFY(a.has_value());
+ VERIFY(any_cast<RequiresInitList>(a).sum == 21);
+ }
+ }
+
+ // equivalence tests
+ {
+ any a, b;
+ VERIFY(!a.has_value() == !b.has_value());
+
+ #if EASTL_EXCEPTIONS_ENABLED
+ int bad_any_cast_thrown = 0;
+ try
+ {
+ VERIFY(any_cast<int>(a) == any_cast<int>(b));
+ }
+ catch (eastl::bad_any_cast)
+ {
+ bad_any_cast_thrown++;
+ }
+ VERIFY(bad_any_cast_thrown != 0);
+ #endif
+
+
+ a = 42; b = 24;
+ VERIFY(any_cast<int>(a) != any_cast<int>(b));
+ VERIFY(a.has_value() == b.has_value());
+
+ a = 42; b = 42;
+ VERIFY(any_cast<int>(a) == any_cast<int>(b));
+ VERIFY(a.has_value() == b.has_value());
+ }
+
+ // move tests
+ {
+ any a = string("hello world");
+ VERIFY(any_cast<string&>(a) == "hello world");
+
+ auto s = move(any_cast<string&>(a)); // move string out
+ VERIFY(s == "hello world");
+ VERIFY(any_cast<string&>(a).empty());
+
+ any_cast<string&>(a) = move(s); // move string in
+ VERIFY(any_cast<string&>(a) == "hello world");
+ }
+
+ // nullptr tests
+ {
+ any* a = nullptr;
+ VERIFY(any_cast<int>(a) == nullptr);
+ VERIFY(any_cast<short>(a) == nullptr);
+ VERIFY(any_cast<long>(a) == nullptr);
+ VERIFY(any_cast<string>(a) == nullptr);
+
+ any b;
+ VERIFY(any_cast<short>(&b) == nullptr);
+ VERIFY(any_cast<const short>(&b) == nullptr);
+ VERIFY(any_cast<volatile short>(&b) == nullptr);
+ VERIFY(any_cast<const volatile short>(&b) == nullptr);
+
+ VERIFY(any_cast<short*>(&b) == nullptr);
+ VERIFY(any_cast<const short*>(&b) == nullptr);
+ VERIFY(any_cast<volatile short*>(&b) == nullptr);
+ VERIFY(any_cast<const volatile short*>(&b) == nullptr);
+ }
+
+ // Aligned type tests
+ {
+ {
+ any a = Align16(1337);
+ VERIFY(any_cast<Align16>(a) == Align16(1337));
+ }
+
+ {
+ any a = Align32(1337);
+ VERIFY(any_cast<Align32>(a) == Align32(1337));
+ }
+
+ {
+ any a = Align64(1337);
+ VERIFY(any_cast<Align64>(a) == Align64(1337));
+ }
+ }
+
+ // make_any
+ {
+ {
+ auto a = make_any<int>(42);
+ VERIFY(any_cast<int>(a) == 42);
+ }
+
+ {
+ auto a = make_any<RequiresInitList>(std::initializer_list<int>{1,2,3,4,5,6,7,8});
+ VERIFY(any_cast<RequiresInitList&>(a).sum == 36);
+ }
+ }
+
+ // user reported regression that eastl::any constructor was not decaying the deduced type correctly.
+ {
+ float f = 42.f;
+ eastl::any a(f);
+ VERIFY(any_cast<float>(a) == 42.f);
+ }
+
+ //testing unsafe operations
+ {
+ eastl::any a = 1;
+ int* i = eastl::any_cast<int>(&a);
+ VERIFY((*i) == 1);
+
+ a = 2;
+ int *j = (int*)eastl::unsafe_any_cast<void>(&a);
+ VERIFY((*j) == 2);
+
+ const eastl::any b = 3;
+ const void * p = eastl::unsafe_any_cast<void>(&b);
+ void *q = const_cast<void *>(p);
+ int *r = static_cast<int *>(q);
+ VERIFY((*r) == 3);
+ }
+
+ // user regression when calling the assignment operator
+ {
+ {
+ eastl::any a1;
+ eastl::any a2;
+ VERIFY(a1.has_value() == false);
+ VERIFY(a2.has_value() == false);
+
+ a1 = a2;
+ VERIFY(a1.has_value() == false);
+ VERIFY(a2.has_value() == false);
+ }
+
+ {
+ eastl::any a1 = 42;
+ eastl::any a2;
+ VERIFY(a1.has_value() == true);
+ VERIFY(a2.has_value() == false);
+
+ a1 = a2;
+ VERIFY(a1.has_value() == false);
+ VERIFY(a2.has_value() == false);
+ }
+
+ {
+ eastl::any a1;
+ eastl::any a2 = 42;
+ VERIFY(a1.has_value() == false);
+ VERIFY(a2.has_value() == true);
+
+ a1 = a2;
+ VERIFY(a1.has_value() == true);
+ VERIFY(a2.has_value() == true);
+ VERIFY(any_cast<int>(a1) == 42);
+ VERIFY(any_cast<int>(a2) == 42);
+ }
+ }
+
+ return nErrorCount;
+}
+
+