Serenity Operating System
at master 275 lines 8.4 kB view raw
1/* 2 * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenity.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibTest/TestSuite.h> 8 9#include <AK/RefPtr.h> 10#include <AK/Variant.h> 11 12namespace { 13 14struct Object : public RefCounted<Object> { 15}; 16 17} 18 19TEST_CASE(basic) 20{ 21 Variant<int, DeprecatedString> the_value { 42 }; 22 EXPECT(the_value.has<int>()); 23 EXPECT_EQ(the_value.get<int>(), 42); 24 the_value = DeprecatedString("42"); 25 EXPECT(the_value.has<DeprecatedString>()); 26 EXPECT_EQ(the_value.get<DeprecatedString>(), "42"); 27} 28 29TEST_CASE(visit) 30{ 31 bool correct = false; 32 Variant<int, DeprecatedString, float> the_value { 42.0f }; 33 the_value.visit( 34 [&](int const&) { correct = false; }, 35 [&](DeprecatedString const&) { correct = false; }, 36 [&](float const&) { correct = true; }); 37 EXPECT(correct); 38} 39 40TEST_CASE(visit_const) 41{ 42 bool correct = false; 43 Variant<int, DeprecatedString> const the_value { "42"sv }; 44 45 the_value.visit( 46 [&](DeprecatedString const&) { correct = true; }, 47 [&](auto&) {}, 48 [&](auto const&) {}); 49 50 EXPECT(correct); 51 52 correct = false; 53 auto the_value_but_not_const = the_value; 54 the_value_but_not_const.visit( 55 [&](DeprecatedString const&) { correct = true; }, 56 [&](auto&) {}); 57 58 EXPECT(correct); 59 60 correct = false; 61 the_value_but_not_const.visit( 62 [&]<typename T>(T&) { correct = !IsConst<T>; }); 63 64 EXPECT(correct); 65} 66 67TEST_CASE(destructor) 68{ 69 struct DestructionChecker { 70 explicit DestructionChecker(bool& was_destroyed) 71 : m_was_destroyed(was_destroyed) 72 { 73 } 74 75 ~DestructionChecker() 76 { 77 m_was_destroyed = true; 78 } 79 bool& m_was_destroyed; 80 }; 81 82 bool was_destroyed = false; 83 { 84 Variant<DestructionChecker> test_variant { DestructionChecker { was_destroyed } }; 85 } 86 EXPECT(was_destroyed); 87 88 bool was_destroyed_when_assigned_to = false; 89 Variant<DestructionChecker, int> original { DestructionChecker { was_destroyed_when_assigned_to } }; 90 Variant<DestructionChecker, int> other { 42 }; 91 original = other; 92 EXPECT(was_destroyed_when_assigned_to); 93} 94 95TEST_CASE(move_moves) 96{ 97 struct NoCopy { 98 AK_MAKE_NONCOPYABLE(NoCopy); 99 100 public: 101 NoCopy() = default; 102 NoCopy(NoCopy&&) = default; 103 }; 104 105 Variant<NoCopy, int> first_variant { 42 }; 106 // Should not fail to compile 107 first_variant = NoCopy {}; 108 109 Variant<NoCopy, int> second_variant = move(first_variant); 110 EXPECT(second_variant.has<NoCopy>()); 111} 112 113TEST_CASE(verify_cast) 114{ 115 Variant<i8, i16, i32, i64> one_integer_to_rule_them_all { static_cast<i32>(42) }; 116 auto fake_integer = one_integer_to_rule_them_all.downcast<i8, i32>(); 117 EXPECT(fake_integer.has<i32>()); 118 EXPECT(one_integer_to_rule_them_all.has<i32>()); 119 EXPECT_EQ(fake_integer.get<i32>(), 42); 120 EXPECT_EQ(one_integer_to_rule_them_all.get<i32>(), 42); 121 122 fake_integer = static_cast<i8>(60); 123 one_integer_to_rule_them_all = fake_integer.downcast<i8, i16>().downcast<i8, i32, float>().downcast<i8, i16, i32, i64>(); 124 EXPECT(fake_integer.has<i8>()); 125 EXPECT(one_integer_to_rule_them_all.has<i8>()); 126 EXPECT_EQ(fake_integer.get<i8>(), 60); 127 EXPECT_EQ(one_integer_to_rule_them_all.get<i8>(), 60); 128 129 using SomeFancyType = Variant<i8, i16>; 130 one_integer_to_rule_them_all = fake_integer.downcast<SomeFancyType>(); 131 EXPECT(fake_integer.has<i8>()); 132 EXPECT(one_integer_to_rule_them_all.has<i8>()); 133 EXPECT_EQ(fake_integer.get<i8>(), 60); 134 EXPECT_EQ(one_integer_to_rule_them_all.get<i8>(), 60); 135} 136 137TEST_CASE(moved_from_state) 138{ 139 // Note: This test requires that Vector's moved-from state be consistent 140 // it need not be in a specific state (though as it is currently implemented, 141 // a moved-from vector is the same as a newly-created vector) 142 // This test does not make assumptions about the state itself, but rather that 143 // it remains consistent when done on different instances. 144 // Should this assumption be broken, we should probably switch to defining a local 145 // class that has fixed semantics, but I doubt the moved-from state of Vector will 146 // change any time soon :P 147 Vector<i32> bunch_of_values { 1, 2, 3, 4, 5, 6, 7, 8 }; 148 Variant<Vector<i32>, Empty> optionally_a_bunch_of_values { Vector<i32> { 1, 2, 3, 4, 5, 6, 7, 8 } }; 149 150 { 151 [[maybe_unused]] auto devnull_0 = move(bunch_of_values); 152 [[maybe_unused]] auto devnull_1 = move(optionally_a_bunch_of_values); 153 } 154 155 // The moved-from state should be the same in both cases, and the variant should still contain a moved-from vector. 156 // Note: Use after move is intentional. 157 EXPECT(optionally_a_bunch_of_values.has<Vector<i32>>()); 158 auto same_contents = __builtin_memcmp(&bunch_of_values, &optionally_a_bunch_of_values.get<Vector<i32>>(), sizeof(bunch_of_values)) == 0; 159 EXPECT(same_contents); 160} 161 162TEST_CASE(duplicated_types) 163{ 164 Variant<int, int, int, int> its_just_an_int { 42 }; 165 EXPECT(its_just_an_int.has<int>()); 166 EXPECT_EQ(its_just_an_int.get<int>(), 42); 167} 168 169TEST_CASE(return_values) 170{ 171 using MyVariant = Variant<int, DeprecatedString, float>; 172 { 173 MyVariant the_value { 42.0f }; 174 175 float value = the_value.visit( 176 [&](int const&) { return 1.0f; }, 177 [&](DeprecatedString const&) { return 2.0f; }, 178 [&](float const& f) { return f; }); 179 EXPECT_EQ(value, 42.0f); 180 } 181 { 182 MyVariant the_value { 42 }; 183 184 int value = the_value.visit( 185 [&](int& i) { return i; }, 186 [&](DeprecatedString&) { return 2; }, 187 [&](float&) { return 3; }); 188 EXPECT_EQ(value, 42); 189 } 190 { 191 const MyVariant the_value { "str" }; 192 193 DeprecatedString value = the_value.visit( 194 [&](int const&) { return DeprecatedString { "wrong" }; }, 195 [&](DeprecatedString const& s) { return s; }, 196 [&](float const&) { return DeprecatedString { "wrong" }; }); 197 EXPECT_EQ(value, "str"); 198 } 199} 200 201TEST_CASE(return_values_by_reference) 202{ 203 auto ref = adopt_ref_if_nonnull(new (nothrow) Object()); 204 Variant<int, DeprecatedString, float> the_value { 42.0f }; 205 206 auto& value = the_value.visit( 207 [&](int const&) -> RefPtr<Object>& { return ref; }, 208 [&](DeprecatedString const&) -> RefPtr<Object>& { return ref; }, 209 [&](float const&) -> RefPtr<Object>& { return ref; }); 210 211 EXPECT_EQ(ref, value); 212 EXPECT_EQ(ref->ref_count(), 1u); 213 EXPECT_EQ(value->ref_count(), 1u); 214} 215 216struct HoldsInt { 217 int i; 218}; 219struct HoldsFloat { 220 float f; 221}; 222 223TEST_CASE(copy_assign) 224{ 225 { 226 Variant<int, DeprecatedString, float> the_value { 42.0f }; 227 228 VERIFY(the_value.has<float>()); 229 EXPECT_EQ(the_value.get<float>(), 42.0f); 230 231 int twelve = 12; 232 the_value = twelve; 233 VERIFY(the_value.has<int>()); 234 EXPECT_EQ(the_value.get<int>(), 12); 235 236 the_value = DeprecatedString("Hello, world!"); 237 VERIFY(the_value.has<DeprecatedString>()); 238 EXPECT_EQ(the_value.get<DeprecatedString>(), "Hello, world!"); 239 } 240 { 241 Variant<HoldsInt, DeprecatedString, HoldsFloat> the_value { HoldsFloat { 42.0f } }; 242 243 VERIFY(the_value.has<HoldsFloat>()); 244 EXPECT_EQ(the_value.get<HoldsFloat>().f, 42.0f); 245 246 HoldsInt twelve { 12 }; 247 the_value = twelve; 248 VERIFY(the_value.has<HoldsInt>()); 249 EXPECT_EQ(the_value.get<HoldsInt>().i, 12); 250 251 the_value = DeprecatedString("Hello, world!"); 252 VERIFY(the_value.has<DeprecatedString>()); 253 EXPECT_EQ(the_value.get<DeprecatedString>(), "Hello, world!"); 254 } 255} 256 257TEST_CASE(default_empty) 258{ 259 Variant<Empty, int> my_variant; 260 EXPECT(my_variant.has<Empty>()); 261 EXPECT(!my_variant.has<int>()); 262} 263 264TEST_CASE(type_list_specialization) 265{ 266 EXPECT_EQ((TypeList<Variant<Empty>>::size), 1u); 267 EXPECT_EQ((TypeList<Variant<Empty, int>>::size), 2u); 268 EXPECT_EQ((TypeList<Variant<Empty, int, String>>::size), 3u); 269 270 using MyVariant = Variant<Empty, int, String>; 271 using MyList = TypeList<MyVariant>; 272 EXPECT((IsSame<typename MyList::template Type<0>, Empty>)); 273 EXPECT((IsSame<typename MyList::template Type<1>, int>)); 274 EXPECT((IsSame<typename MyList::template Type<2>, String>)); 275}