/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Daniel Bertalan * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include class NonCopyable { AK_MAKE_NONCOPYABLE(NonCopyable); AK_MAKE_DEFAULT_MOVABLE(NonCopyable); public: constexpr NonCopyable() { } ~NonCopyable() = default; int x { 13 }; }; class NonTriviallyCopyable { AK_MAKE_DEFAULT_COPYABLE(NonTriviallyCopyable); AK_MAKE_DEFAULT_MOVABLE(NonTriviallyCopyable); public: constexpr NonTriviallyCopyable() = default; ~NonTriviallyCopyable() = default; ByteString x { "13" }; }; class TriviallyCopyable { AK_MAKE_DEFAULT_COPYABLE(TriviallyCopyable); AK_MAKE_DEFAULT_MOVABLE(TriviallyCopyable); public: constexpr TriviallyCopyable() = default; ~TriviallyCopyable() = default; int x { 13 }; }; TEST_CASE(basic_optional) { Optional x; EXPECT_EQ(x.has_value(), false); x = 3; EXPECT_EQ(x.has_value(), true); EXPECT_EQ(x.value(), 3); } TEST_CASE(move_optional) { Optional x; EXPECT_EQ(x.has_value(), false); x = 3; EXPECT_EQ(x.has_value(), true); EXPECT_EQ(x.value(), 3); Optional y; y = move(x); EXPECT_EQ(y.has_value(), true); EXPECT_EQ(y.value(), 3); EXPECT_EQ(x.has_value(), false); } TEST_CASE(optional_rvalue_ref_qualified_getters) { auto make_an_optional = []() -> Optional { return NonCopyable {}; }; EXPECT_EQ(make_an_optional().value().x, 13); EXPECT_EQ(make_an_optional().value_or(NonCopyable {}).x, 13); } TEST_CASE(optional_leak_1) { struct Structure { Optional str; }; // This used to leak, it does not anymore. Vector vec; vec.append({ "foo" }); EXPECT_EQ(vec[0].str.has_value(), true); EXPECT_EQ(vec[0].str.value(), "foo"); } TEST_CASE(short_notation) { Optional value = "foo"sv; EXPECT_EQ(value->length(), 3u); EXPECT_EQ(*value, "foo"); } TEST_CASE(comparison_without_values) { Optional opt0; Optional opt1; Optional opt2; EXPECT_EQ(opt0, opt1); EXPECT_EQ(opt0, opt2); } TEST_CASE(comparison_with_values) { Optional opt0; Optional opt1 = "foo"sv; Optional opt2 = "foo"sv; Optional opt3 = "bar"sv; EXPECT_NE(opt0, opt1); EXPECT_EQ(opt1, opt2); EXPECT_NE(opt1, opt3); } TEST_CASE(comparison_to_underlying_types) { Optional opt0; EXPECT_NE(opt0, ByteString()); EXPECT_NE(opt0, "foo"); Optional opt1 = "foo"sv; EXPECT_EQ(opt1, "foo"); EXPECT_NE(opt1, "bar"); EXPECT_EQ(opt1, ByteString("foo")); } TEST_CASE(comparison_with_numeric_types) { Optional opt0; EXPECT_NE(opt0, 0); Optional opt1 = 7; EXPECT_EQ(opt1, 7); EXPECT_EQ(opt1, 7.0); EXPECT_EQ(opt1, 7u); EXPECT_NE(opt1, -2); } TEST_CASE(test_constexpr) { int i = 13; NonCopyable dcm {}; EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional {}); EXPECT_CONSTEVAL(Optional { 13 }); EXPECT_CONSTEVAL(Optional { NonCopyable {} }); EXPECT_CONSTEVAL(Optional { 13 }); EXPECT_CONSTEVAL(Optional { NonCopyable {} }); EXPECT_CONSTEVAL(Optional { i }); EXPECT_CONSTEVAL(Optional { dcm }); EXPECT_CONSTEVAL(Optional { 13 }); EXPECT_CONSTEVAL(Optional { NonCopyable {} }); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(!Optional {}.has_value()); static_assert(Optional { 13 }.has_value()); static_assert(Optional { NonCopyable {} }.has_value()); static_assert(Optional { 13 }.has_value()); static_assert(Optional { NonCopyable {} }.has_value()); static_assert(Optional { i }.has_value()); static_assert(Optional { dcm }.has_value()); static_assert(Optional { 13 }.has_value()); static_assert(Optional { NonCopyable {} }.has_value()); static_assert(Optional { 13 }.value() == 13); static_assert(Optional { NonCopyable {} }.value().x == 13); static_assert(Optional { 13 }.value() == 13); static_assert(Optional { 13 }.value() == 13); static_assert(Optional { NonCopyable {} }.value().x == 13); static_assert(!(Optional { 1 } = {}).has_value(), "Assigning a `{}` should clear the Optional, even for scalar types^^"); } TEST_CASE(test_copy_ctor_and_dtor_called) { #ifdef AK_HAVE_CONDITIONALLY_TRIVIAL static_assert(IsTriviallyDestructible>); static_assert(IsTriviallyCopyable>); static_assert(IsTriviallyCopyConstructible>); static_assert(IsTriviallyCopyAssignable>); // These can't be trivial as we have to clear the original object. static_assert(!IsTriviallyMoveConstructible>); static_assert(!IsTriviallyMoveAssignable>); #endif struct DestructionChecker { explicit DestructionChecker(bool& was_destroyed) : m_was_destroyed(was_destroyed) { } ~DestructionChecker() { m_was_destroyed = true; } bool& m_was_destroyed; }; static_assert(!IsTriviallyDestructible>); bool was_destroyed = false; { Optional test_optional = DestructionChecker { was_destroyed }; } EXPECT(was_destroyed); struct CopyChecker { explicit CopyChecker(bool& was_copy_constructed) : m_was_copy_constructed(was_copy_constructed) { } CopyChecker(CopyChecker const& other) : m_was_copy_constructed(other.m_was_copy_constructed) { m_was_copy_constructed = true; } bool& m_was_copy_constructed; }; static_assert(IsCopyConstructible>); static_assert(!IsTriviallyCopyConstructible>); bool was_copy_constructed = false; Optional copy1 = CopyChecker { was_copy_constructed }; Optional copy2 = copy1; EXPECT(was_copy_constructed); struct MoveChecker { explicit MoveChecker(bool& was_move_constructed) : m_was_move_constructed(was_move_constructed) { } MoveChecker(MoveChecker const& other) : m_was_move_constructed(other.m_was_move_constructed) { EXPECT(false); } MoveChecker(MoveChecker&& other) : m_was_move_constructed(other.m_was_move_constructed) { m_was_move_constructed = true; } bool& m_was_move_constructed; }; static_assert(IsMoveConstructible>); static_assert(!IsTriviallyMoveConstructible>); bool was_moved = false; Optional move1 = MoveChecker { was_moved }; Optional move2 = move(move1); EXPECT(was_moved); #ifdef AK_HAVE_CONDITIONALLY_TRIVIAL struct NonDestructible { ~NonDestructible() = delete; }; static_assert(!IsDestructible>); #endif } TEST_CASE(basic_optional_reference) { Optional x; EXPECT_EQ(x.has_value(), false); int a = 3; x = a; EXPECT_EQ(x.has_value(), true); EXPECT_EQ(x.value(), 3); EXPECT_EQ(&x.value(), &a); Optional y; EXPECT_EQ(y.has_value(), false); int b = 3; y = b; EXPECT_EQ(y.has_value(), true); EXPECT_EQ(y.value(), 3); EXPECT_EQ(&y.value(), &b); static_assert(IsConst>); } TEST_CASE(move_optional_reference) { Optional x; EXPECT_EQ(x.has_value(), false); int b = 3; x = b; EXPECT_EQ(x.has_value(), true); EXPECT_EQ(x.value(), 3); Optional y; y = move(x); EXPECT_EQ(y.has_value(), true); EXPECT_EQ(y.value(), 3); } TEST_CASE(short_notation_reference) { StringView test = "foo"sv; Optional value = test; EXPECT_EQ(value->length(), 3u); EXPECT_EQ(*value, "foo"); } TEST_CASE(comparison_reference) { StringView test = "foo"sv; Optional opt0; Optional opt1 = test; Optional opt2 = "foo"sv; Optional opt3 = "bar"sv; EXPECT_NE(opt0, opt1); EXPECT_EQ(opt1, opt2); EXPECT_NE(opt1, opt3); } TEST_CASE(uninitialized_constructor) { static bool was_constructed = false; struct Internal { Internal() { was_constructed = true; } }; struct ShouldNotBeDefaultConstructed { bool m_default_constructed { true }; Internal m_internal; ShouldNotBeDefaultConstructed() = default; ShouldNotBeDefaultConstructed(bool) : m_default_constructed(false) { } }; static_assert(IsConstructible); Optional opt; EXPECT(!was_constructed); EXPECT(!opt.has_value()); opt = ShouldNotBeDefaultConstructed { true }; EXPECT(was_constructed); EXPECT(opt.has_value()); EXPECT(!opt.value().m_default_constructed); } consteval bool test_constexpr() { Optional none; if (none.has_value()) return false; Optional x; x = 3; if (!x.has_value()) return false; if (x.value() != 3) return false; Optional y; y = x.release_value(); if (!y.has_value()) return false; if (y.value() != 3) return false; if (x.has_value()) return false; return true; } static_assert(test_constexpr()); template struct CheckAssignments; template requires(requires { declval() = declval(); }) struct CheckAssignments { static constexpr bool allowed = true; }; template requires(!requires { declval() = declval(); }) struct CheckAssignments { static constexpr bool allowed = false; }; static_assert(CheckAssignments, int>::allowed); static_assert(!CheckAssignments, double*>::allowed); static_assert(CheckAssignments, int&>::allowed); static_assert(!CheckAssignments, int const&>::allowed); static_assert(!CheckAssignments, int&&>::allowed); static_assert(!CheckAssignments, int const&&>::allowed); static_assert(CheckAssignments, int&>::allowed); static_assert(CheckAssignments, int const&>::allowed); static_assert(CheckAssignments, int&&>::allowed); // Lifetime extension static_assert(CheckAssignments, int const&&>::allowed); // Lifetime extension static_assert(CheckAssignments, NonTriviallyCopyable&>::allowed); static_assert(CheckAssignments, NonTriviallyCopyable const&>::allowed); static_assert(CheckAssignments, NonTriviallyCopyable&&>::allowed); // Lifetime extension static_assert(CheckAssignments, NonTriviallyCopyable const&&>::allowed); // Lifetime extension static_assert(CheckAssignments, TriviallyCopyable>::allowed); static_assert(CheckAssignments, TriviallyCopyable const&>::allowed); static_assert(CheckAssignments, Optional>::allowed); static_assert(CheckAssignments, Optional>::allowed); static_assert(CheckAssignments, Optional>::allowed); static_assert(CheckAssignments, NonTriviallyCopyable>::allowed); static_assert(CheckAssignments, NonTriviallyCopyable const&>::allowed); static_assert(CheckAssignments, Optional>::allowed); static_assert(CheckAssignments, Optional>::allowed); static_assert(!CheckAssignments, Optional>::allowed); TEST_CASE(nontrivially_copyable_assignment) { { TriviallyCopyable x {}; Optional y = x; Optional z = y; // Can copy implicitly EXPECT_EQ(z->x, 13); } { NonTriviallyCopyable x {}; Optional y = x; Optional z = y.copy(); // Have to copy explicitly EXPECT_EQ(z->x, "13"); } { NonTriviallyCopyable x {}; Optional y = x; Optional z = Optional(y); // Explicit copy constructor is still defined EXPECT_EQ(z->x, "13"); } } TEST_CASE(string_specialization) { EXPECT_EQ(sizeof(Optional), sizeof(String)); { Optional foo; EXPECT(!foo.has_value()); foo = "long_enough_to_be_allocated"_string; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo = "initial_value"_string; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "initial_value"sv); foo = "long_enough_to_be_allocated"_string; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT(!foo.has_value()); String bar = "long_enough_to_be_allocated"_string; foo = bar; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT(!foo.has_value()); Optional bar = "long_enough_to_be_allocated"_string; foo = bar; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); EXPECT(bar.has_value()); EXPECT_EQ(bar.value(), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT(!foo.has_value()); foo = Optional { "long_enough_to_be_allocated"_string }; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo = "long_enough_to_be_allocated"_string; EXPECT_EQ(foo.value_or("fallback_value"_string), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT_EQ(foo.value_or("fallback_value"_string), "fallback_value"sv); } { EXPECT_EQ((Optional { "long_enough_to_be_allocated"_string }).value_or("fallback_value"_string), "long_enough_to_be_allocated"sv); } { EXPECT_EQ((Optional {}).value_or("fallback_value"_string), "fallback_value"sv); } } TEST_CASE(flystring_specialization) { EXPECT_EQ(sizeof(Optional), sizeof(FlyString)); { Optional foo; EXPECT(!foo.has_value()); foo = "long_enough_to_be_allocated"_fly_string; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo = "initial_value"_fly_string; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "initial_value"sv); foo = "long_enough_to_be_allocated"_fly_string; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT(!foo.has_value()); FlyString bar = "long_enough_to_be_allocated"_fly_string; foo = bar; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT(!foo.has_value()); Optional bar = "long_enough_to_be_allocated"_fly_string; foo = bar; EXPECT(bar.has_value()); EXPECT_EQ(bar.value(), "long_enough_to_be_allocated"sv); EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT(!foo.has_value()); foo = Optional { "long_enough_to_be_allocated"_fly_string }; EXPECT(foo.has_value()); EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv); } { Optional foo = "long_enough_to_be_allocated"_fly_string; EXPECT_EQ(foo.value_or("fallback_value"_fly_string), "long_enough_to_be_allocated"sv); } { Optional foo; EXPECT_EQ(foo.value_or("fallback_value"_fly_string), "fallback_value"sv); } { EXPECT_EQ((Optional { "long_enough_to_be_allocated"_fly_string }).value_or("fallback_value"_fly_string), "long_enough_to_be_allocated"sv); } { EXPECT_EQ((Optional {}).value_or("fallback_value"_fly_string), "fallback_value"sv); } }