Serenity Operating System
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}