1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/containers/enum_set.h"
6
7 #include <stddef.h>
8
9 #include <optional>
10
11 #include "base/containers/to_vector.h"
12 #include "base/test/gtest_util.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest-death-test.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace base {
18 namespace {
19
20 enum class TestEnum {
21 kTestBelowMinNegative = -1,
22 kTestBelowMin = 0,
23 kTest1 = 1,
24 kTestMin = kTest1,
25 kTest2,
26 kTest3,
27 kTest4,
28 kTest5,
29 kTestMax = kTest5,
30 kTest6OutOfBounds,
31 kTest7OutOfBounds
32 };
33 using TestEnumSet = EnumSet<TestEnum, TestEnum::kTestMin, TestEnum::kTestMax>;
34
35 enum class TestEnumExtreme {
36 kTest0 = 0,
37 kTestMin = kTest0,
38 kTest63 = 63,
39 kTestMax = kTest63,
40 kTest64OutOfBounds,
41 };
42 using TestEnumExtremeSet = EnumSet<TestEnumExtreme,
43 TestEnumExtreme::kTestMin,
44 TestEnumExtreme::kTestMax>;
45
46 class EnumSetTest : public ::testing::Test {};
47 class EnumSetDeathTest : public ::testing::Test {};
48
TEST_F(EnumSetTest,ClassConstants)49 TEST_F(EnumSetTest, ClassConstants) {
50 EXPECT_EQ(TestEnum::kTestMin, TestEnumSet::kMinValue);
51 EXPECT_EQ(TestEnum::kTestMax, TestEnumSet::kMaxValue);
52 EXPECT_EQ(5u, TestEnumSet::kValueCount);
53 }
54
55 // Use static_assert to check that functions we expect to be compile time
56 // evaluatable are really that way.
TEST_F(EnumSetTest,ConstexprsAreValid)57 TEST_F(EnumSetTest, ConstexprsAreValid) {
58 static_assert(TestEnumSet::All().Has(TestEnum::kTest2),
59 "Expected All() to be integral constant expression");
60 static_assert(TestEnumSet::FromRange(TestEnum::kTest2, TestEnum::kTest4)
61 .Has(TestEnum::kTest2),
62 "Expected FromRange() to be integral constant expression");
63 static_assert(TestEnumSet{TestEnum::kTest2}.Has(TestEnum::kTest2),
64 "Expected TestEnumSet() to be integral constant expression");
65 static_assert(
66 TestEnumSet::FromEnumBitmask(1 << static_cast<uint64_t>(TestEnum::kTest2))
67 .Has(TestEnum::kTest2),
68 "Expected TestEnumSet() to be integral constant expression");
69 }
70
TEST_F(EnumSetTest,DefaultConstructor)71 TEST_F(EnumSetTest, DefaultConstructor) {
72 const TestEnumSet enums;
73 EXPECT_TRUE(enums.empty());
74 EXPECT_EQ(0u, enums.size());
75 EXPECT_FALSE(enums.Has(TestEnum::kTest1));
76 EXPECT_FALSE(enums.Has(TestEnum::kTest2));
77 EXPECT_FALSE(enums.Has(TestEnum::kTest3));
78 EXPECT_FALSE(enums.Has(TestEnum::kTest4));
79 EXPECT_FALSE(enums.Has(TestEnum::kTest5));
80 }
81
TEST_F(EnumSetTest,OneArgConstructor)82 TEST_F(EnumSetTest, OneArgConstructor) {
83 const TestEnumSet enums = {TestEnum::kTest4};
84 EXPECT_FALSE(enums.empty());
85 EXPECT_EQ(1u, enums.size());
86 EXPECT_FALSE(enums.Has(TestEnum::kTest1));
87 EXPECT_FALSE(enums.Has(TestEnum::kTest2));
88 EXPECT_FALSE(enums.Has(TestEnum::kTest3));
89 EXPECT_TRUE(enums.Has(TestEnum::kTest4));
90 EXPECT_FALSE(enums.Has(TestEnum::kTest5));
91 }
92
TEST_F(EnumSetTest,OneArgConstructorSize)93 TEST_F(EnumSetTest, OneArgConstructorSize) {
94 TestEnumExtremeSet enums = {TestEnumExtreme::kTest0};
95 EXPECT_TRUE(enums.Has(TestEnumExtreme::kTest0));
96 }
97
TEST_F(EnumSetTest,TwoArgConstructor)98 TEST_F(EnumSetTest, TwoArgConstructor) {
99 const TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest2};
100 EXPECT_FALSE(enums.empty());
101 EXPECT_EQ(2u, enums.size());
102 EXPECT_FALSE(enums.Has(TestEnum::kTest1));
103 EXPECT_TRUE(enums.Has(TestEnum::kTest2));
104 EXPECT_FALSE(enums.Has(TestEnum::kTest3));
105 EXPECT_TRUE(enums.Has(TestEnum::kTest4));
106 EXPECT_FALSE(enums.Has(TestEnum::kTest5));
107 }
108
TEST_F(EnumSetTest,ThreeArgConstructor)109 TEST_F(EnumSetTest, ThreeArgConstructor) {
110 const TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest2,
111 TestEnum::kTest1};
112 EXPECT_FALSE(enums.empty());
113 EXPECT_EQ(3u, enums.size());
114 EXPECT_TRUE(enums.Has(TestEnum::kTest1));
115 EXPECT_TRUE(enums.Has(TestEnum::kTest2));
116 EXPECT_FALSE(enums.Has(TestEnum::kTest3));
117 EXPECT_TRUE(enums.Has(TestEnum::kTest4));
118 EXPECT_FALSE(enums.Has(TestEnum::kTest5));
119 }
120
TEST_F(EnumSetTest,DuplicatesInConstructor)121 TEST_F(EnumSetTest, DuplicatesInConstructor) {
122 EXPECT_EQ(
123 TestEnumSet({TestEnum::kTest4, TestEnum::kTest2, TestEnum::kTest1,
124 TestEnum::kTest4, TestEnum::kTest2, TestEnum::kTest4}),
125 TestEnumSet({TestEnum::kTest1, TestEnum::kTest2, TestEnum::kTest4}));
126 }
127
TEST_F(EnumSetTest,All)128 TEST_F(EnumSetTest, All) {
129 const TestEnumSet enums(TestEnumSet::All());
130 EXPECT_FALSE(enums.empty());
131 EXPECT_EQ(5u, enums.size());
132 EXPECT_TRUE(enums.Has(TestEnum::kTest1));
133 EXPECT_TRUE(enums.Has(TestEnum::kTest2));
134 EXPECT_TRUE(enums.Has(TestEnum::kTest3));
135 EXPECT_TRUE(enums.Has(TestEnum::kTest4));
136 EXPECT_TRUE(enums.Has(TestEnum::kTest5));
137 }
138
TEST_F(EnumSetTest,AllExtreme)139 TEST_F(EnumSetTest, AllExtreme) {
140 const TestEnumExtremeSet enums(TestEnumExtremeSet::All());
141 EXPECT_FALSE(enums.empty());
142 EXPECT_EQ(64u, enums.size());
143 EXPECT_TRUE(enums.Has(TestEnumExtreme::kTest0));
144 EXPECT_TRUE(enums.Has(TestEnumExtreme::kTest63));
145 EXPECT_FALSE(enums.Has(TestEnumExtreme::kTest64OutOfBounds));
146 }
147
TEST_F(EnumSetTest,FromRange)148 TEST_F(EnumSetTest, FromRange) {
149 EXPECT_EQ(TestEnumSet({TestEnum::kTest2, TestEnum::kTest3, TestEnum::kTest4}),
150 TestEnumSet::FromRange(TestEnum::kTest2, TestEnum::kTest4));
151 EXPECT_EQ(TestEnumSet::All(),
152 TestEnumSet::FromRange(TestEnum::kTest1, TestEnum::kTest5));
153 EXPECT_EQ(TestEnumSet({TestEnum::kTest2}),
154 TestEnumSet::FromRange(TestEnum::kTest2, TestEnum::kTest2));
155
156 using RestrictedRangeSet =
157 EnumSet<TestEnum, TestEnum::kTest2, TestEnum::kTestMax>;
158 EXPECT_EQ(RestrictedRangeSet(
159 {TestEnum::kTest2, TestEnum::kTest3, TestEnum::kTest4}),
160 RestrictedRangeSet::FromRange(TestEnum::kTest2, TestEnum::kTest4));
161 EXPECT_EQ(RestrictedRangeSet::All(),
162 RestrictedRangeSet::FromRange(TestEnum::kTest2, TestEnum::kTest5));
163 }
164
TEST_F(EnumSetTest,Put)165 TEST_F(EnumSetTest, Put) {
166 TestEnumSet enums = {TestEnum::kTest4};
167 enums.Put(TestEnum::kTest3);
168 EXPECT_EQ(TestEnumSet({TestEnum::kTest3, TestEnum::kTest4}), enums);
169 enums.Put(TestEnum::kTest5);
170 EXPECT_EQ(TestEnumSet({TestEnum::kTest3, TestEnum::kTest4, TestEnum::kTest5}),
171 enums);
172 }
173
TEST_F(EnumSetTest,PutAll)174 TEST_F(EnumSetTest, PutAll) {
175 TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest5};
176 enums.PutAll({TestEnum::kTest3, TestEnum::kTest4});
177 EXPECT_EQ(TestEnumSet({TestEnum::kTest3, TestEnum::kTest4, TestEnum::kTest5}),
178 enums);
179 }
180
TEST_F(EnumSetTest,PutRange)181 TEST_F(EnumSetTest, PutRange) {
182 TestEnumSet enums;
183 enums.PutRange(TestEnum::kTest2, TestEnum::kTest4);
184 EXPECT_EQ(TestEnumSet({TestEnum::kTest2, TestEnum::kTest3, TestEnum::kTest4}),
185 enums);
186 }
187
TEST_F(EnumSetTest,RetainAll)188 TEST_F(EnumSetTest, RetainAll) {
189 TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest5};
190 enums.RetainAll(TestEnumSet({TestEnum::kTest3, TestEnum::kTest4}));
191 EXPECT_EQ(TestEnumSet({TestEnum::kTest4}), enums);
192 }
193
TEST_F(EnumSetTest,Remove)194 TEST_F(EnumSetTest, Remove) {
195 TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest5};
196 enums.Remove(TestEnum::kTest1);
197 enums.Remove(TestEnum::kTest3);
198 EXPECT_EQ(TestEnumSet({TestEnum::kTest4, TestEnum::kTest5}), enums);
199 enums.Remove(TestEnum::kTest4);
200 EXPECT_EQ(TestEnumSet({TestEnum::kTest5}), enums);
201 enums.Remove(TestEnum::kTest5);
202 enums.Remove(TestEnum::kTest6OutOfBounds);
203 EXPECT_TRUE(enums.empty());
204 }
205
TEST_F(EnumSetTest,RemoveAll)206 TEST_F(EnumSetTest, RemoveAll) {
207 TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest5};
208 enums.RemoveAll(TestEnumSet({TestEnum::kTest3, TestEnum::kTest4}));
209 EXPECT_EQ(TestEnumSet({TestEnum::kTest5}), enums);
210 }
211
TEST_F(EnumSetTest,Clear)212 TEST_F(EnumSetTest, Clear) {
213 TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest5};
214 enums.Clear();
215 EXPECT_TRUE(enums.empty());
216 }
217
TEST_F(EnumSetTest,Set)218 TEST_F(EnumSetTest, Set) {
219 TestEnumSet enums;
220 EXPECT_TRUE(enums.empty());
221
222 enums.PutOrRemove(TestEnum::kTest3, false);
223 EXPECT_TRUE(enums.empty());
224
225 enums.PutOrRemove(TestEnum::kTest4, true);
226 EXPECT_EQ(enums, TestEnumSet({TestEnum::kTest4}));
227
228 enums.PutOrRemove(TestEnum::kTest5, true);
229 EXPECT_EQ(enums, TestEnumSet({TestEnum::kTest4, TestEnum::kTest5}));
230 enums.PutOrRemove(TestEnum::kTest5, true);
231 EXPECT_EQ(enums, TestEnumSet({TestEnum::kTest4, TestEnum::kTest5}));
232
233 enums.PutOrRemove(TestEnum::kTest4, false);
234 EXPECT_EQ(enums, TestEnumSet({TestEnum::kTest5}));
235 }
236
TEST_F(EnumSetTest,Has)237 TEST_F(EnumSetTest, Has) {
238 const TestEnumSet enums = {TestEnum::kTest4, TestEnum::kTest5};
239 EXPECT_FALSE(enums.Has(TestEnum::kTest1));
240 EXPECT_FALSE(enums.Has(TestEnum::kTest2));
241 EXPECT_FALSE(enums.Has(TestEnum::kTest3));
242 EXPECT_TRUE(enums.Has(TestEnum::kTest4));
243 EXPECT_TRUE(enums.Has(TestEnum::kTest5));
244 EXPECT_FALSE(enums.Has(TestEnum::kTest6OutOfBounds));
245 }
246
TEST_F(EnumSetTest,HasAll)247 TEST_F(EnumSetTest, HasAll) {
248 const TestEnumSet enums1 = {TestEnum::kTest4, TestEnum::kTest5};
249 const TestEnumSet enums2 = {TestEnum::kTest3, TestEnum::kTest4};
250 const TestEnumSet enums3 = Union(enums1, enums2);
251 EXPECT_TRUE(enums1.HasAll(enums1));
252 EXPECT_FALSE(enums1.HasAll(enums2));
253 EXPECT_FALSE(enums1.HasAll(enums3));
254
255 EXPECT_FALSE(enums2.HasAll(enums1));
256 EXPECT_TRUE(enums2.HasAll(enums2));
257 EXPECT_FALSE(enums2.HasAll(enums3));
258
259 EXPECT_TRUE(enums3.HasAll(enums1));
260 EXPECT_TRUE(enums3.HasAll(enums2));
261 EXPECT_TRUE(enums3.HasAll(enums3));
262 }
263
TEST_F(EnumSetTest,HasAny)264 TEST_F(EnumSetTest, HasAny) {
265 const TestEnumSet enums1 = {TestEnum::kTest4, TestEnum::kTest5};
266 const TestEnumSet enums2 = {TestEnum::kTest3, TestEnum::kTest4};
267 const TestEnumSet enums3 = {TestEnum::kTest1, TestEnum::kTest2};
268 EXPECT_TRUE(enums1.HasAny(enums1));
269 EXPECT_TRUE(enums1.HasAny(enums2));
270 EXPECT_FALSE(enums1.HasAny(enums3));
271
272 EXPECT_TRUE(enums2.HasAny(enums1));
273 EXPECT_TRUE(enums2.HasAny(enums2));
274 EXPECT_FALSE(enums2.HasAny(enums3));
275
276 EXPECT_FALSE(enums3.HasAny(enums1));
277 EXPECT_FALSE(enums3.HasAny(enums2));
278 EXPECT_TRUE(enums3.HasAny(enums3));
279 }
280
TEST_F(EnumSetTest,Iterators)281 TEST_F(EnumSetTest, Iterators) {
282 const TestEnumSet enums1 = {TestEnum::kTest4, TestEnum::kTest5};
283 TestEnumSet enums2;
284 for (TestEnum e : enums1) {
285 enums2.Put(e);
286 }
287 EXPECT_EQ(enums2, enums1);
288 }
289
TEST_F(EnumSetTest,RangeBasedForLoop)290 TEST_F(EnumSetTest, RangeBasedForLoop) {
291 const TestEnumSet enums1 = {TestEnum::kTest2, TestEnum::kTest5};
292 TestEnumSet enums2;
293 for (TestEnum e : enums1) {
294 enums2.Put(e);
295 }
296 EXPECT_EQ(enums2, enums1);
297 }
298
TEST_F(EnumSetTest,IteratorComparisonOperators)299 TEST_F(EnumSetTest, IteratorComparisonOperators) {
300 const TestEnumSet enums = {TestEnum::kTest2, TestEnum::kTest4};
301 const auto first_it = enums.begin();
302 const auto second_it = ++enums.begin();
303
304 // Copy for equality testing.
305 const auto first_it_copy = first_it;
306
307 // Sanity check, as the rest of the test relies on |first_it| and
308 // |first_it_copy| pointing to the same element and |first_it| and |second_it|
309 // pointing to different elements.
310 ASSERT_EQ(*first_it, *first_it_copy);
311 ASSERT_NE(*first_it, *second_it);
312
313 EXPECT_TRUE(first_it == first_it_copy);
314 EXPECT_FALSE(first_it != first_it_copy);
315
316 EXPECT_TRUE(first_it != second_it);
317 EXPECT_FALSE(first_it == second_it);
318 }
319
TEST_F(EnumSetTest,IteratorIncrementOperators)320 TEST_F(EnumSetTest, IteratorIncrementOperators) {
321 const TestEnumSet enums = {TestEnum::kTest2, TestEnum::kTest4};
322 const auto begin = enums.begin();
323
324 auto post_inc_it = begin;
325 auto pre_inc_it = begin;
326
327 auto post_inc_return_it = post_inc_it++;
328 auto pre_inc_return_it = ++pre_inc_it;
329
330 // |pre_inc_it| and |post_inc_it| should point to the same element.
331 EXPECT_EQ(pre_inc_it, post_inc_it);
332 EXPECT_EQ(*pre_inc_it, *post_inc_it);
333
334 // |pre_inc_it| should NOT point to the first element.
335 EXPECT_NE(begin, pre_inc_it);
336 EXPECT_NE(*begin, *pre_inc_it);
337
338 // |post_inc_it| should NOT point to the first element.
339 EXPECT_NE(begin, post_inc_it);
340 EXPECT_NE(*begin, *post_inc_it);
341
342 // Prefix increment should return new iterator.
343 EXPECT_EQ(pre_inc_return_it, post_inc_it);
344 EXPECT_EQ(*pre_inc_return_it, *post_inc_it);
345
346 // Postfix increment should return original iterator.
347 EXPECT_EQ(post_inc_return_it, begin);
348 EXPECT_EQ(*post_inc_return_it, *begin);
349 }
350
TEST_F(EnumSetTest,Union)351 TEST_F(EnumSetTest, Union) {
352 const TestEnumSet enums1 = {TestEnum::kTest4, TestEnum::kTest5};
353 const TestEnumSet enums2 = {TestEnum::kTest3, TestEnum::kTest4};
354 const TestEnumSet enums3 = Union(enums1, enums2);
355
356 EXPECT_EQ(TestEnumSet({TestEnum::kTest3, TestEnum::kTest4, TestEnum::kTest5}),
357 enums3);
358 }
359
TEST_F(EnumSetTest,Intersection)360 TEST_F(EnumSetTest, Intersection) {
361 const TestEnumSet enums1 = {TestEnum::kTest4, TestEnum::kTest5};
362 const TestEnumSet enums2 = {TestEnum::kTest3, TestEnum::kTest4};
363 const TestEnumSet enums3 = Intersection(enums1, enums2);
364
365 EXPECT_EQ(TestEnumSet({TestEnum::kTest4}), enums3);
366 }
367
TEST_F(EnumSetTest,Difference)368 TEST_F(EnumSetTest, Difference) {
369 const TestEnumSet enums1 = {TestEnum::kTest4, TestEnum::kTest5};
370 const TestEnumSet enums2 = {TestEnum::kTest3, TestEnum::kTest4};
371 const TestEnumSet enums3 = Difference(enums1, enums2);
372
373 EXPECT_EQ(TestEnumSet({TestEnum::kTest5}), enums3);
374 }
375
TEST_F(EnumSetTest,ToFromEnumBitmask)376 TEST_F(EnumSetTest, ToFromEnumBitmask) {
377 const TestEnumSet empty;
378 EXPECT_EQ(empty.ToEnumBitmask(), 0ULL);
379 EXPECT_EQ(TestEnumSet::FromEnumBitmask(0), empty);
380
381 const TestEnumSet enums1 = {TestEnum::kTest2};
382 const uint64_t val1 = 1ULL << static_cast<uint64_t>(TestEnum::kTest2);
383 EXPECT_EQ(enums1.ToEnumBitmask(), val1);
384 EXPECT_EQ(TestEnumSet::FromEnumBitmask(val1), enums1);
385
386 const TestEnumSet enums2 = {TestEnum::kTest3, TestEnum::kTest4};
387 const uint64_t val2 = 1ULL << static_cast<uint64_t>(TestEnum::kTest3) |
388 1ULL << static_cast<uint64_t>(TestEnum::kTest4);
389 EXPECT_EQ(enums2.ToEnumBitmask(), val2);
390 EXPECT_EQ(TestEnumSet::FromEnumBitmask(val2), enums2);
391 }
392
TEST_F(EnumSetTest,ToFromEnumBitmaskExtreme)393 TEST_F(EnumSetTest, ToFromEnumBitmaskExtreme) {
394 const TestEnumExtremeSet empty;
395 EXPECT_EQ(empty.ToEnumBitmask(), 0ULL);
396 EXPECT_EQ(TestEnumExtremeSet::FromEnumBitmask(0ULL), empty);
397
398 const TestEnumExtremeSet enums1 = {TestEnumExtreme::kTest63};
399 const uint64_t val1 = 1ULL << static_cast<uint64_t>(TestEnumExtreme::kTest63);
400 EXPECT_EQ(enums1.ToEnumBitmask(), val1);
401 EXPECT_EQ(TestEnumExtremeSet::FromEnumBitmask(val1), enums1);
402 }
403
TEST_F(EnumSetTest,FromEnumBitmaskIgnoresExtraBits)404 TEST_F(EnumSetTest, FromEnumBitmaskIgnoresExtraBits) {
405 const TestEnumSet kSets[] = {
406 {},
407 {TestEnum::kTestMin},
408 {TestEnum::kTestMax},
409 {TestEnum::kTestMin, TestEnum::kTestMax},
410 {TestEnum::kTestMin, TestEnum::kTestMax},
411 {TestEnum::kTest2, TestEnum::kTest4},
412 };
413 size_t i = 0;
414 for (const TestEnumSet& set : kSets) {
415 SCOPED_TRACE(i++);
416 const uint64_t val = set.ToEnumBitmask();
417
418 // Produce a bitstring for a single enum value. When `e` is in range
419 // relative to TestEnumSet, this function behaves identically to
420 // `single_val_bitstring`. When `e` is not in range, this function attempts
421 // to compute a value, while `single_val_bitstring` intentionally crashes.
422 auto single_val_bitstring = [](TestEnum e) -> uint64_t {
423 uint64_t shift_amount = static_cast<uint64_t>(e);
424 // Shifting left more than the number of bits in the lhs would be UB.
425 CHECK_LT(shift_amount, sizeof(uint64_t) * 8);
426 return 1ULL << shift_amount;
427 };
428
429 const uint64_t kJunkVals[] = {
430 // Add junk bits above kTestMax.
431 val | single_val_bitstring(TestEnum::kTest6OutOfBounds),
432 val | single_val_bitstring(TestEnum::kTest7OutOfBounds),
433 val | single_val_bitstring(TestEnum::kTest6OutOfBounds) |
434 single_val_bitstring(TestEnum::kTest7OutOfBounds),
435 // Add junk bits below kTestMin.
436 val | single_val_bitstring(TestEnum::kTestBelowMin),
437 };
438 for (uint64_t junk_val : kJunkVals) {
439 SCOPED_TRACE(junk_val);
440 ASSERT_NE(val, junk_val);
441
442 const TestEnumSet set_from_junk = TestEnumSet::FromEnumBitmask(junk_val);
443 EXPECT_EQ(set_from_junk, set);
444 EXPECT_EQ(set_from_junk.ToEnumBitmask(), set.ToEnumBitmask());
445
446 // Iterating both sets should produce the same sequence.
447 auto it1 = set.begin();
448 auto it2 = set_from_junk.begin();
449 while (it1 != set.end() && it2 != set_from_junk.end()) {
450 EXPECT_EQ(*it1, *it2);
451 ++it1;
452 ++it2;
453 }
454 EXPECT_TRUE(it1 == set.end());
455 EXPECT_TRUE(it2 == set_from_junk.end());
456 }
457 }
458 }
459
TEST_F(EnumSetTest,OneEnumValue)460 TEST_F(EnumSetTest, OneEnumValue) {
461 enum class TestEnumOne {
462 kTest1 = 1,
463 kTestMin = kTest1,
464 kTestMax = kTest1,
465 };
466 using TestEnumOneSet =
467 EnumSet<TestEnumOne, TestEnumOne::kTestMin, TestEnumOne::kTestMax>;
468 EXPECT_EQ(TestEnumOne::kTestMin, TestEnumOneSet::kMinValue);
469 EXPECT_EQ(TestEnumOne::kTestMax, TestEnumOneSet::kMaxValue);
470 EXPECT_EQ(1u, TestEnumOneSet::kValueCount);
471 }
472
TEST_F(EnumSetTest,SparseEnum)473 TEST_F(EnumSetTest, SparseEnum) {
474 enum class TestEnumSparse {
475 kTest1 = 1,
476 kTestMin = 1,
477 kTest50 = 50,
478 kTest100 = 100,
479 kTestMax = kTest100,
480 };
481 using TestEnumSparseSet = EnumSet<TestEnumSparse, TestEnumSparse::kTestMin,
482 TestEnumSparse::kTestMax>;
483 TestEnumSparseSet sparse;
484 sparse.Put(TestEnumSparse::kTestMin);
485 sparse.Put(TestEnumSparse::kTestMax);
486 EXPECT_EQ(sparse.size(), 2u);
487
488 EXPECT_EQ(TestEnumSparseSet::All().size(), 100u);
489 }
490
TEST_F(EnumSetTest,GetNth64bitWordBitmaskFromEnum)491 TEST_F(EnumSetTest, GetNth64bitWordBitmaskFromEnum) {
492 enum class TestEnumEdgeCase {
493 kTest1 = 1,
494 kTestMin = kTest1,
495 kTest63 = 63,
496 kTest64 = 64,
497 kTest100 = 100,
498 kTestMax = kTest100,
499 };
500 using TestEnumEdgeCaseSet =
501 EnumSet<TestEnumEdgeCase, TestEnumEdgeCase::kTestMin,
502 TestEnumEdgeCase::kTestMax>;
503 TestEnumEdgeCaseSet sparse;
504 sparse.Put(TestEnumEdgeCase::kTest1);
505 sparse.Put(TestEnumEdgeCase::kTest63);
506 sparse.Put(TestEnumEdgeCase::kTest64);
507 sparse.Put(TestEnumEdgeCase::kTest100);
508 std::optional<uint64_t> bit_mask_0 = sparse.GetNth64bitWordBitmask(0);
509 ASSERT_TRUE(bit_mask_0.has_value());
510 ASSERT_EQ(bit_mask_0.value(),
511 1ull << static_cast<uint32_t>(TestEnumEdgeCase::kTest1) |
512 1ull << static_cast<uint32_t>(TestEnumEdgeCase::kTest63));
513 std::optional<uint64_t> bit_mask_1 = sparse.GetNth64bitWordBitmask(1);
514 ASSERT_TRUE(bit_mask_1.has_value());
515 ASSERT_EQ(
516 bit_mask_1.value(),
517 1ull << (static_cast<uint32_t>(TestEnumEdgeCase::kTest64) - 64u) |
518 1ull << (static_cast<uint32_t>(TestEnumEdgeCase::kTest100) - 64u));
519 std::optional<uint64_t> bit_mask_2 = sparse.GetNth64bitWordBitmask(2);
520 ASSERT_FALSE(bit_mask_2.has_value());
521 }
522
TEST_F(EnumSetTest,SparseEnumSmall)523 TEST_F(EnumSetTest, SparseEnumSmall) {
524 enum class TestEnumSparse {
525 kTest1 = 1,
526 kTestMin = 1,
527 kTest50 = 50,
528 kTest60 = 60,
529 kTestMax = kTest60,
530 };
531 using TestEnumSparseSet = EnumSet<TestEnumSparse, TestEnumSparse::kTestMin,
532 TestEnumSparse::kTestMax>;
533 TestEnumSparseSet sparse;
534 sparse.Put(TestEnumSparse::kTestMin);
535 sparse.Put(TestEnumSparse::kTestMax);
536 EXPECT_EQ(sparse.size(), 2u);
537
538 // This may seem a little surprising! There are only 3 distinct values in
539 // TestEnumSparse, so why does TestEnumSparseSet think it has 60 of them? This
540 // is an artifact of EnumSet's design, as it has no way of knowing which
541 // values between the min and max are actually named in the enum's definition.
542 EXPECT_EQ(TestEnumSparseSet::All().size(), 60u);
543 }
544
TEST_F(EnumSetDeathTest,CrashesOnOutOfRange)545 TEST_F(EnumSetDeathTest, CrashesOnOutOfRange) {
546 EXPECT_CHECK_DEATH(TestEnumSet({TestEnum::kTestBelowMin}));
547 EXPECT_CHECK_DEATH(TestEnumSet({TestEnum::kTest6OutOfBounds}));
548 EXPECT_CHECK_DEATH(TestEnumSet({TestEnum::kTest7OutOfBounds}));
549 }
550
TEST_F(EnumSetDeathTest,EnumWithNegatives)551 TEST_F(EnumSetDeathTest, EnumWithNegatives) {
552 enum class TestEnumNeg {
553 kTestBelowMin = -3,
554 kTestA = -2,
555 kTestMin = kTestA,
556 kTestB = -1,
557 kTestC = 0,
558 kTestD = 1,
559 kTestE = 2,
560 kTestMax = kTestE,
561 kTestF = 3,
562 };
563 // This EnumSet starts negative and ends positive.
564 using TestEnumWithNegSet =
565 EnumSet<TestEnumNeg, TestEnumNeg::kTestMin, TestEnumNeg::kTestMax>;
566
567 // Should crash because kTestBelowMin is not in range.
568 EXPECT_CHECK_DEATH(TestEnumWithNegSet({TestEnumNeg::kTestBelowMin}));
569 // kTestD is in range, but note that kTestMin is negative. This should work.
570 EXPECT_TRUE(
571 TestEnumWithNegSet({TestEnumNeg::kTestD}).Has(TestEnumNeg::kTestD));
572 // Even though kTestA is negative, it is in range, so this should work.
573 EXPECT_TRUE(
574 TestEnumWithNegSet({TestEnumNeg::kTestA}).Has(TestEnumNeg::kTestA));
575 }
576
TEST_F(EnumSetDeathTest,EnumWithOnlyNegatives)577 TEST_F(EnumSetDeathTest, EnumWithOnlyNegatives) {
578 enum class TestEnumNeg {
579 kTestBelowMin = -10,
580 kTestA = -9,
581 kTestMin = kTestA,
582 kTestB = -8,
583 kTestC = -7,
584 kTestD = -6,
585 kTestMax = kTestD,
586 kTestF = -5,
587 };
588 // This EnumSet starts negative and ends negative.
589 using TestEnumWithNegSet =
590 EnumSet<TestEnumNeg, TestEnumNeg::kTestMin, TestEnumNeg::kTestMax>;
591
592 // Should crash because kTestBelowMin is not in range.
593 EXPECT_CHECK_DEATH(TestEnumWithNegSet({TestEnumNeg::kTestBelowMin}));
594 // kTestA, kTestD are in range, but note that kTestMin and values are
595 // negative. This should work.
596 EXPECT_TRUE(
597 TestEnumWithNegSet({TestEnumNeg::kTestA}).Has(TestEnumNeg::kTestA));
598 EXPECT_TRUE(
599 TestEnumWithNegSet({TestEnumNeg::kTestD}).Has(TestEnumNeg::kTestD));
600 }
601
TEST_F(EnumSetDeathTest,VariadicConstructorCrashesOnOutOfRange)602 TEST_F(EnumSetDeathTest, VariadicConstructorCrashesOnOutOfRange) {
603 // Constructor should crash given out-of-range values.
604 EXPECT_CHECK_DEATH(TestEnumSet({TestEnum::kTestBelowMin}).empty());
605 EXPECT_CHECK_DEATH(TestEnumSet({TestEnum::kTestBelowMinNegative}).empty());
606 EXPECT_CHECK_DEATH(TestEnumSet({TestEnum::kTest6OutOfBounds}).empty());
607 }
608
TEST_F(EnumSetDeathTest,FromRangeCrashesOnBadInputs)609 TEST_F(EnumSetDeathTest, FromRangeCrashesOnBadInputs) {
610 // FromRange crashes when the bounds are in range, but out of order.
611 EXPECT_CHECK_DEATH(
612 TestEnumSet().FromRange(TestEnum::kTest3, TestEnum::kTest1));
613
614 // FromRange crashes when the start value is out of range.
615 EXPECT_CHECK_DEATH(
616 TestEnumSet().FromRange(TestEnum::kTestBelowMin, TestEnum::kTest1));
617 EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::kTestBelowMinNegative,
618 TestEnum::kTest1));
619 EXPECT_CHECK_DEATH(
620 TestEnumSet().FromRange(TestEnum::kTest6OutOfBounds, TestEnum::kTest1));
621
622 // FromRange crashes when the end value is out of range.
623 EXPECT_CHECK_DEATH(
624 TestEnumSet().FromRange(TestEnum::kTest3, TestEnum::kTestBelowMin));
625 EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::kTest3,
626 TestEnum::kTestBelowMinNegative));
627 EXPECT_CHECK_DEATH(
628 TestEnumSet().FromRange(TestEnum::kTest3, TestEnum::kTest6OutOfBounds));
629
630 // Crashes when start and end are both out of range.
631 EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::kTest6OutOfBounds,
632 TestEnum::kTest7OutOfBounds));
633 EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::kTest6OutOfBounds,
634 TestEnum::kTest7OutOfBounds));
635 }
636
TEST_F(EnumSetDeathTest,PutCrashesOnOutOfRange)637 TEST_F(EnumSetDeathTest, PutCrashesOnOutOfRange) {
638 EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::kTestBelowMin));
639 EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::kTestBelowMinNegative));
640 EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::kTest6OutOfBounds));
641 EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::kTest7OutOfBounds));
642 }
643
TEST_F(EnumSetDeathTest,PutRangeCrashesOnBadInputs)644 TEST_F(EnumSetDeathTest, PutRangeCrashesOnBadInputs) {
645 // Crashes when one input is out of range.
646 EXPECT_CHECK_DEATH(TestEnumSet().PutRange(TestEnum::kTestBelowMinNegative,
647 TestEnum::kTestBelowMin));
648 EXPECT_CHECK_DEATH(
649 TestEnumSet().PutRange(TestEnum::kTest3, TestEnum::kTest7OutOfBounds));
650
651 // Crashes when both inputs are out of range.
652 EXPECT_CHECK_DEATH(TestEnumSet().PutRange(TestEnum::kTest6OutOfBounds,
653 TestEnum::kTest7OutOfBounds));
654
655 // Crashes when inputs are out of order.
656 EXPECT_CHECK_DEATH(
657 TestEnumSet().PutRange(TestEnum::kTest2, TestEnum::kTest1));
658 }
659
TEST_F(EnumSetTest,ToStringEmpty)660 TEST_F(EnumSetTest, ToStringEmpty) {
661 const TestEnumSet enums;
662 EXPECT_THAT(enums.ToString(), testing::Eq("00000"));
663 }
664
TEST_F(EnumSetTest,ToString)665 TEST_F(EnumSetTest, ToString) {
666 const TestEnumSet enums = {TestEnum::kTest4};
667 EXPECT_THAT(enums.ToString(), testing::Eq("01000"));
668 }
669
TEST_F(EnumSetTest,ToVectorEmpty)670 TEST_F(EnumSetTest, ToVectorEmpty) {
671 const TestEnumSet enums;
672 EXPECT_TRUE(ToVector(enums).empty());
673 }
674
TEST_F(EnumSetTest,ToVector)675 TEST_F(EnumSetTest, ToVector) {
676 const TestEnumSet enums = {TestEnum::kTest2, TestEnum::kTest4};
677 EXPECT_THAT(ToVector(enums),
678 testing::ElementsAre(TestEnum::kTest2, TestEnum::kTest4));
679 }
680
681 } // namespace
682 } // namespace base
683