/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Daniel Bertalan * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include 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) { struct DontCopyMe { DontCopyMe() { } ~DontCopyMe() = default; DontCopyMe(DontCopyMe&&) = default; DontCopyMe& operator=(DontCopyMe&&) = default; DontCopyMe(DontCopyMe const&) = delete; DontCopyMe& operator=(DontCopyMe const&) = delete; int x { 13 }; }; auto make_an_optional = []() -> Optional { return DontCopyMe {}; }; EXPECT_EQ(make_an_optional().value().x, 13); EXPECT_EQ(make_an_optional().value_or(DontCopyMe {}).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"; 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"; Optional opt2 = "foo"; Optional opt3 = "bar"; EXPECT_NE(opt0, opt1); EXPECT_EQ(opt1, opt2); EXPECT_NE(opt1, opt3); } TEST_CASE(comparison_to_underlying_types) { Optional opt0; EXPECT_NE(opt0, String()); EXPECT_NE(opt0, "foo"); Optional opt1 = "foo"; EXPECT_EQ(opt1, "foo"); EXPECT_NE(opt1, "bar"); EXPECT_EQ(opt1, String("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_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(const CopyChecker& 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(const MoveChecker& 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 }