aboutsummaryrefslogtreecommitdiff
path: root/test/source/TestOptional.cpp
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2021-04-08 16:43:58 +0200
committerToni Uhlig <matzeton@googlemail.com>2021-04-08 16:43:58 +0200
commite59cf7b09e7388d369e8d2bf73501cde79c28708 (patch)
tree6099307032bb86f4a969721f9ac447d3d1be67d4 /test/source/TestOptional.cpp
Squashed 'EASTL/' content from commit fad5471
git-subtree-dir: EASTL git-subtree-split: fad54717f8e4ebb13b20095da7efd07a53af0f10
Diffstat (limited to 'test/source/TestOptional.cpp')
-rw-r--r--test/source/TestOptional.cpp653
1 files changed, 653 insertions, 0 deletions
diff --git a/test/source/TestOptional.cpp b/test/source/TestOptional.cpp
new file mode 100644
index 0000000..6c1fa4f
--- /dev/null
+++ b/test/source/TestOptional.cpp
@@ -0,0 +1,653 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+#include "EASTLTest.h"
+#include <EASTL/type_traits.h>
+#include <EASTL/sort.h>
+#include <EASTL/vector.h>
+#include <EASTL/string.h>
+#include <EASTL/optional.h>
+#include <EASTL/unique_ptr.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+struct IntStruct
+{
+ IntStruct(int in) : data(in) {}
+ int data;
+};
+
+bool operator<(const IntStruct& lhs, const IntStruct& rhs)
+ { return lhs.data < rhs.data; }
+bool operator==(const IntStruct& lhs, const IntStruct& rhs)
+ { return lhs.data == rhs.data; }
+
+
+/////////////////////////////////////////////////////////////////////////////
+struct destructor_test
+{
+ ~destructor_test() { destructor_ran = true; }
+ static bool destructor_ran;
+ static void reset() { destructor_ran = false; }
+};
+bool destructor_test::destructor_ran = false;
+
+/////////////////////////////////////////////////////////////////////////////
+struct move_test
+{
+ move_test() = default;
+ move_test(move_test&&) { was_moved = true; }
+ move_test& operator=(move_test&&) { was_moved = true; return *this;}
+
+ // issue a compiler error is container tries to copy
+ move_test(move_test const&) = delete;
+ move_test& operator=(const move_test&) = delete;
+
+ static bool was_moved;
+};
+
+bool move_test::was_moved = false;
+
+/////////////////////////////////////////////////////////////////////////////
+template <typename T>
+class forwarding_test
+{
+ eastl::optional<T> m_optional;
+
+public:
+ forwarding_test() : m_optional() {}
+ forwarding_test(T&& t) : m_optional(t) {}
+ ~forwarding_test() { m_optional.reset(); }
+
+ template <typename U>
+ T GetValueOrDefault(U&& def) const
+ {
+ return m_optional.value_or(eastl::forward<U>(def));
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+struct assignment_test
+{
+ assignment_test() { ++num_objects_inited; }
+ assignment_test(assignment_test&&) { ++num_objects_inited; }
+ assignment_test(const assignment_test&) { ++num_objects_inited; }
+ assignment_test& operator=(assignment_test&&) { return *this; }
+ assignment_test& operator=(const assignment_test&) { return *this; }
+ ~assignment_test() { --num_objects_inited; }
+
+ static int num_objects_inited;
+};
+
+int assignment_test::num_objects_inited = 0;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// TestOptional
+//
+int TestOptional()
+{
+ using namespace eastl;
+ int nErrorCount = 0;
+ #if defined(EASTL_OPTIONAL_ENABLED) && EASTL_OPTIONAL_ENABLED
+ {
+ {
+ VERIFY( (is_same<optional<int>::value_type, int>::value));
+ VERIFY( (is_same<optional<short>::value_type, short>::value));
+ VERIFY(!(is_same<optional<short>::value_type, long>::value));
+ VERIFY( (is_same<optional<const short>::value_type, const short>::value));
+ VERIFY( (is_same<optional<volatile short>::value_type, volatile short>::value));
+ VERIFY( (is_same<optional<const volatile short>::value_type, const volatile short>::value));
+
+ VERIFY(is_empty<nullopt_t>::value);
+ #if EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE
+ VERIFY(is_literal_type<nullopt_t>::value);
+ #endif
+
+ #if EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE
+ VERIFY(is_trivially_destructible<int>::value);
+ VERIFY(is_trivially_destructible<Internal::optional_storage<int>>::value);
+ VERIFY(is_trivially_destructible<optional<int>>::value);
+ VERIFY(is_trivially_destructible<optional<int>>::value == is_trivially_destructible<int>::value);
+ #endif
+
+ {
+ struct NotTrivialDestructible { ~NotTrivialDestructible() {} };
+ VERIFY(!is_trivially_destructible<NotTrivialDestructible>::value);
+ VERIFY(!is_trivially_destructible<optional<NotTrivialDestructible>>::value);
+ VERIFY(!is_trivially_destructible<Internal::optional_storage<NotTrivialDestructible>>::value);
+ VERIFY(is_trivially_destructible<optional<NotTrivialDestructible>>::value == is_trivially_destructible<NotTrivialDestructible>::value);
+ }
+ }
+
+ {
+ optional<int> o;
+ VERIFY(!o);
+ VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D);
+ o = 1024;
+ VERIFY(static_cast<bool>(o));
+ VERIFY(o.value_or(0x8BADF00D) == 1024);
+ VERIFY(o.value() == 1024);
+
+ // Test reset
+ o.reset();
+ VERIFY(!o);
+ VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D);
+ }
+
+ {
+ optional<int> o(nullopt);
+ VERIFY(!o);
+ VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D);
+ }
+
+ {
+ optional<int> o = {};
+ VERIFY(!o);
+ VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D);
+ }
+
+ {
+ optional<int> o(42);
+ VERIFY(bool(o));
+ VERIFY(o.value_or(0x8BADF00D) == 42);
+ o = nullopt;
+ VERIFY(!o);
+ VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D);
+ }
+
+ {
+ optional<int> o(42);
+ VERIFY(static_cast<bool>(o));
+ VERIFY(o.value_or(0x8BADF00D) == 42);
+ VERIFY(o.value() == 42);
+ }
+
+ {
+ auto o = make_optional(42);
+ VERIFY((is_same<decltype(o), optional<int>>::value));
+ VERIFY(static_cast<bool>(o));
+ VERIFY(o.value_or(0x8BADF00D) == 42);
+ VERIFY(o.value() == 42);
+ }
+
+ {
+ int a = 42;
+ auto o = make_optional(a);
+ VERIFY((is_same<decltype(o)::value_type, int>::value));
+ VERIFY(o.value() == 42);
+ }
+
+ {
+ // test make_optional stripping refs/cv-qualifers
+ int a = 42;
+ const volatile int& intRef = a;
+ auto o = make_optional(intRef);
+ VERIFY((is_same<decltype(o)::value_type, int>::value));
+ VERIFY(o.value() == 42);
+ }
+
+ {
+ int a = 10;
+ const volatile int& aRef = a;
+ auto o = eastl::make_optional(aRef);
+ VERIFY(o.value() == 10);
+ }
+
+ {
+ {
+ struct local { int payload1; };
+ auto o = eastl::make_optional<local>(42);
+ VERIFY(o.value().payload1 == 42);
+ }
+ {
+ struct local { int payload1; int payload2; };
+ auto o = eastl::make_optional<local>(42, 43);
+ VERIFY(o.value().payload1 == 42);
+ VERIFY(o.value().payload2 == 43);
+ }
+
+ {
+ struct local
+ {
+ local(std::initializer_list<int> ilist)
+ {
+ payload1 = ilist.begin()[0];
+ payload2 = ilist.begin()[1];
+ }
+
+ int payload1;
+ int payload2;
+ };
+
+ auto o = eastl::make_optional<local>({42, 43});
+ VERIFY(o.value().payload1 == 42);
+ VERIFY(o.value().payload2 == 43);
+ }
+ }
+
+ {
+ optional<int> o1(42), o2(24);
+ VERIFY(o1.value() == 42);
+ VERIFY(o2.value() == 24);
+ VERIFY(*o1 == 42);
+ VERIFY(*o2 == 24);
+ o1 = eastl::move(o2);
+ VERIFY(*o2 == 24);
+ VERIFY(*o1 == 24);
+ VERIFY(o2.value() == 24);
+ VERIFY(o1.value() == 24);
+ VERIFY(bool(o1));
+ VERIFY(bool(o2));
+ }
+
+ {
+ struct local { int payload; };
+ optional<local> o = local{ 42 };
+ VERIFY(o->payload == 42);
+ }
+
+ {
+ struct local
+ {
+ int test() const { return 42; }
+ };
+
+ {
+ const optional<local> o = local{};
+ VERIFY(o->test() == 42);
+ VERIFY((*o).test() == 42);
+ VERIFY(o.value().test() == 42);
+ VERIFY(bool(o));
+ }
+
+ {
+ optional<local> o = local{};
+ VERIFY(bool(o));
+ o = nullopt;
+ VERIFY(!bool(o));
+
+ VERIFY(o.value_or(local{}).test() == 42);
+ VERIFY(!bool(o));
+ }
+ }
+ }
+
+ {
+ move_test t;
+ optional<move_test> o(eastl::move(t));
+ VERIFY(move_test::was_moved);
+ }
+
+ {
+ forwarding_test<float>ft(1.f);
+ float val = ft.GetValueOrDefault(0.f);
+ VERIFY(val == 1.f);
+ }
+
+ {
+ assignment_test::num_objects_inited = 0;
+ {
+ optional<assignment_test> o1;
+ optional<assignment_test> o2 = assignment_test();
+ optional<assignment_test> o3(o2);
+ VERIFY(assignment_test::num_objects_inited == 2);
+ o1 = nullopt;
+ VERIFY(assignment_test::num_objects_inited == 2);
+ o1 = o2;
+ VERIFY(assignment_test::num_objects_inited == 3);
+ o1 = o2;
+ VERIFY(assignment_test::num_objects_inited == 3);
+ o1 = nullopt;
+ VERIFY(assignment_test::num_objects_inited == 2);
+ o2 = o1;
+ VERIFY(assignment_test::num_objects_inited == 1);
+ o1 = o2;
+ VERIFY(assignment_test::num_objects_inited == 1);
+ }
+ VERIFY(assignment_test::num_objects_inited == 0);
+
+ {
+ optional<assignment_test> o1;
+ VERIFY(assignment_test::num_objects_inited == 0);
+ o1 = nullopt;
+ VERIFY(assignment_test::num_objects_inited == 0);
+ o1 = optional<assignment_test>(assignment_test());
+ VERIFY(assignment_test::num_objects_inited == 1);
+ o1 = optional<assignment_test>(assignment_test());
+ VERIFY(assignment_test::num_objects_inited == 1);
+ optional<assignment_test> o2(eastl::move(o1));
+ VERIFY(assignment_test::num_objects_inited == 2);
+ o1 = nullopt;
+ VERIFY(assignment_test::num_objects_inited == 1);
+ }
+ VERIFY(assignment_test::num_objects_inited == 0);
+ }
+
+ #if EASTL_VARIADIC_TEMPLATES_ENABLED
+ {
+ struct vec3
+ {
+ vec3(std::initializer_list<float> ilist) { auto* p = ilist.begin(); x = *p++; y = *p++; z = *p++; }
+ vec3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} // testing variadic template constructor overload
+ float x = 0, y = 0, z = 0;
+ };
+
+ {
+ optional<vec3> o{ in_place, 4.f, 5.f, 6.f };
+ VERIFY(o->x == 4 && o->y == 5 && o->z == 6);
+ }
+
+ {
+ optional<vec3> o{ in_place, {4.f, 5.f, 6.f} };
+ VERIFY(o->x == 4 && o->y == 5 && o->z == 6);
+ }
+
+ {
+ optional<string> o(in_place, {'a', 'b', 'c'});
+ VERIFY(o == string("abc"));
+ }
+
+ // http://en.cppreference.com/w/cpp/utility/optional/emplace
+ {
+ optional<vec3> o;
+ o.emplace(42.f, 42.f, 42.f);
+ VERIFY(o->x == 42.f && o->y == 42.f && o->z == 42.f);
+ }
+
+ {
+ optional<vec3> o;
+ o.emplace({42.f, 42.f, 42.f});
+ VERIFY(o->x == 42.f && o->y == 42.f && o->z == 42.f);
+ }
+
+ {
+ optional<int> o;
+ o.emplace(42);
+ VERIFY(*o == 42);
+ }
+
+ struct nonCopyableNonMovable
+ {
+ nonCopyableNonMovable(int v) : val(v) {}
+
+ nonCopyableNonMovable(const nonCopyableNonMovable&) = delete;
+ nonCopyableNonMovable(nonCopyableNonMovable&&) = delete;
+ nonCopyableNonMovable& operator=(const nonCopyableNonMovable&) = delete;
+
+ int val = 0;
+ };
+
+ {
+ optional<nonCopyableNonMovable> o;
+ o.emplace(42);
+ VERIFY(o->val == 42);
+ }
+
+ {
+ // Verify emplace will destruct object if it has been engaged.
+ destructor_test::reset();
+ optional<destructor_test> o;
+ o.emplace();
+ VERIFY(!destructor_test::destructor_ran);
+
+ destructor_test::reset();
+ o.emplace();
+ VERIFY(destructor_test::destructor_ran);
+ }
+ }
+ #endif
+
+
+ // swap
+ {
+ {
+ optional<int> o1 = 42, o2 = 24;
+ VERIFY(*o1 == 42);
+ VERIFY(*o2 == 24);
+ o1.swap(o2);
+ VERIFY(*o1 == 24);
+ VERIFY(*o2 == 42);
+ }
+
+ {
+ optional<int> o1 = 42, o2 = 24;
+ VERIFY(*o1 == 42);
+ VERIFY(*o2 == 24);
+ swap(o1, o2);
+ VERIFY(*o1 == 24);
+ VERIFY(*o2 == 42);
+ }
+
+ {
+ optional<int> o1 = 42, o2;
+ VERIFY(*o1 == 42);
+ VERIFY(o2.has_value() == false);
+ swap(o1, o2);
+ VERIFY(o1.has_value() == false);
+ VERIFY(*o2 == 42);
+ }
+
+ {
+ optional<int> o1 = nullopt, o2 = 42;
+ VERIFY(o1.has_value() == false);
+ VERIFY(*o2 == 42);
+ swap(o1, o2);
+ VERIFY(*o1 == 42);
+ VERIFY(o2.has_value() == false);
+ }
+ }
+
+ {
+ optional<IntStruct> o(in_place, 10);
+ optional<IntStruct> e;
+
+ VERIFY(o < IntStruct(42));
+ VERIFY(!(o < IntStruct(2)));
+ VERIFY(!(o < IntStruct(10)));
+ VERIFY(e < o);
+ VERIFY(e < IntStruct(10));
+
+ VERIFY(o > IntStruct(4));
+ VERIFY(!(o > IntStruct(42)));
+
+ VERIFY(o >= IntStruct(4));
+ VERIFY(o >= IntStruct(10));
+ VERIFY(IntStruct(4) <= o);
+ VERIFY(IntStruct(10) <= o);
+
+ VERIFY(o == IntStruct(10));
+ VERIFY(o->data == IntStruct(10).data);
+
+ VERIFY(o != IntStruct(11));
+ VERIFY(o->data != IntStruct(11).data);
+
+ VERIFY(e == nullopt);
+ VERIFY(nullopt == e);
+
+ VERIFY(o != nullopt);
+ VERIFY(nullopt != o);
+ VERIFY(nullopt < o);
+ VERIFY(o > nullopt);
+ VERIFY(!(nullopt > o));
+ VERIFY(!(o < nullopt));
+ VERIFY(nullopt <= o);
+ VERIFY(o >= nullopt);
+ }
+
+ // hash
+ {
+ {
+ // verify that the hash an empty eastl::optional object is zero.
+ typedef hash<optional<int>> hash_optional_t;
+ optional<int> e;
+ VERIFY(hash_optional_t{}(e) == 0);
+ }
+
+ {
+ // verify that the hash is the same as the hash of the underlying type
+ const char* const pMessage = "Electronic Arts Canada";
+ typedef hash<optional<string>> hash_optional_t;
+ optional<string> o = string(pMessage);
+ VERIFY(hash_optional_t{}(o) == hash<string>{}(pMessage));
+ }
+ }
+
+ // sorting
+ {
+ vector<optional<int>> v = {{122}, {115}, nullopt, {223}};
+ sort(begin(v), end(v));
+ vector<optional<int>> sorted = {nullopt, 115, 122, 223};
+
+ VERIFY(v == sorted);
+ }
+
+ // test destructors being called.
+ {
+ destructor_test::reset();
+ {
+ optional<destructor_test> o = destructor_test{};
+ }
+ VERIFY(destructor_test::destructor_ran);
+
+ destructor_test::reset();
+ {
+ optional<destructor_test> o;
+ }
+ // destructor shouldn't be called as object wasn't constructed.
+ VERIFY(!destructor_test::destructor_ran);
+
+
+ destructor_test::reset();
+ {
+ optional<destructor_test> o = {};
+ }
+ // destructor shouldn't be called as object wasn't constructed.
+ VERIFY(!destructor_test::destructor_ran);
+
+ destructor_test::reset();
+ {
+ optional<destructor_test> o = nullopt;
+ }
+ // destructor shouldn't be called as object wasn't constructed.
+ VERIFY(!destructor_test::destructor_ran);
+ }
+
+ // optional rvalue tests
+ {
+ VERIFY(*optional<int>(1) == 1);
+ VERIFY( optional<int>(1).value() == 1);
+ VERIFY( optional<int>(1).value_or(0xdeadf00d) == 1);
+ VERIFY( optional<int>().value_or(0xdeadf00d) == 0xdeadf00d);
+ VERIFY( optional<int>(1).has_value() == true);
+ VERIFY( optional<int>().has_value() == false);
+ VERIFY( optional<IntStruct>(in_place, 10)->data == 10);
+
+ }
+
+ // alignment type tests
+ {
+ static_assert(alignof(optional<Align16>) == alignof(Align16), "optional alignment failure");
+ static_assert(alignof(optional<Align32>) == alignof(Align32), "optional alignment failure");
+ static_assert(alignof(optional<Align64>) == alignof(Align64), "optional alignment failure");
+ }
+
+ {
+ // user reported regression that failed to compile
+ struct local_struct
+ {
+ local_struct() {}
+ ~local_struct() {}
+ };
+ static_assert(!eastl::is_trivially_destructible_v<local_struct>, "");
+
+ {
+ local_struct ls;
+ eastl::optional<local_struct> o{ls};
+ }
+ {
+ const local_struct ls;
+ eastl::optional<local_struct> o{ls};
+ }
+ }
+
+ {
+ {
+ // user regression
+ eastl::optional<eastl::string> o = eastl::string("Hello World");
+ eastl::optional<eastl::string> co;
+
+ co = o; // force copy-assignment
+
+ VERIFY( o.value().data() != co.value().data());
+ VERIFY( o.value().data() == eastl::string("Hello World"));
+ VERIFY(co.value().data() == eastl::string("Hello World"));
+ }
+ {
+ // user regression
+ EA_DISABLE_VC_WARNING(4625 4626) // copy/assignment operator constructor was implicitly defined as deleted
+ struct local
+ {
+ eastl::unique_ptr<int> ptr;
+ };
+ EA_RESTORE_VC_WARNING()
+
+ eastl::optional<local> o1 = local{eastl::make_unique<int>(42)};
+ eastl::optional<local> o2;
+
+ o2 = eastl::move(o1);
+
+ VERIFY(!!o1 == true);
+ VERIFY(!!o2 == true);
+ VERIFY(!!o1->ptr == false);
+ VERIFY(!!o2->ptr == true);
+ VERIFY(o2->ptr.get() != nullptr);
+ }
+ {
+ // user regression
+ static bool copyCtorCalledWithUninitializedValue;
+ static bool moveCtorCalledWithUninitializedValue;
+ copyCtorCalledWithUninitializedValue = moveCtorCalledWithUninitializedValue = false;
+ struct local
+ {
+ int val;
+ local()
+ : val(0xabcdabcd)
+ {}
+ local(const local& other)
+ : val(other.val)
+ {
+ if (other.val != 0xabcdabcd)
+ copyCtorCalledWithUninitializedValue = true;
+ }
+ local(local&& other)
+ : val(eastl::move(other.val))
+ {
+ if (other.val != 0xabcdabcd)
+ moveCtorCalledWithUninitializedValue = true;
+ }
+ local& operator=(const local&) = delete;
+ };
+ eastl::optional<local> n;
+ eastl::optional<local> o1(n);
+ VERIFY(!copyCtorCalledWithUninitializedValue);
+ eastl::optional<local> o2(eastl::move(n));
+ VERIFY(!moveCtorCalledWithUninitializedValue);
+ }
+ }
+
+ {
+ auto testFn = []() -> optional<int>
+ {
+ return eastl::nullopt;
+ };
+
+ auto o = testFn();
+ VERIFY(!!o == false);
+ }
+
+ #endif // EASTL_OPTIONAL_ENABLED
+ return nErrorCount;
+}
+