Project
Loading...
Searching...
No Matches
testEnumFlags.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#define BOOST_TEST_MODULE Test Flags
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15
16#include <boost/test/unit_test.hpp>
17#include <boost/preprocessor/arithmetic/inc.hpp>
18#include <boost/preprocessor/repetition/repeat.hpp>
19
20#include <stdexcept>
21#include <string>
22
24
25// Example enum to use with EnumFlags
26enum class TestEnum : uint8_t {
27 Bit1 = 0,
28 Bit2,
29 Bit3,
30 Bit4,
32};
33
34// Very long enum
35// to test that it works beyond 32 bits upto 64 bits
36#define ENUM_BIT_NAME(n) Bit##n
37#define ENUM_BIT_NAME_EXPAND(n) ENUM_BIT_NAME(n)
38#define ENUM_BIT(z, n, _) ENUM_BIT_NAME_EXPAND(BOOST_PP_INC(n)) = (n),
39enum class TestEnumLong : uint64_t {
40 BOOST_PP_REPEAT(64, ENUM_BIT, _)
41};
42#undef ENUM_BIT
43#undef ENUM_BIT_NAME
44#undef ENUM_BIT_NAME_EXPAND
45
47{
48 using EFlags = o2::utils::EnumFlags<TestEnum>;
49
50 // Test default initialization
51 EFlags flags;
52 BOOST_TEST(flags.None == 0);
53 BOOST_TEST(flags.All == 31);
54 BOOST_TEST(flags.value() == 0);
55 BOOST_TEST(!flags.any());
56
57 // Test initialization with a single flag
58 EFlags flag1(TestEnum::Bit1);
59 BOOST_TEST(flag1.test(TestEnum::Bit1));
60 BOOST_TEST(!flag1.test(TestEnum::Bit2));
61 BOOST_TEST(flag1.value() == (1 << static_cast<unsigned int>(TestEnum::Bit1)));
62
63 // Test initialization with initializer list
64 EFlags multipleFlags({TestEnum::Bit1, TestEnum::Bit3});
65 BOOST_TEST(multipleFlags.test(TestEnum::Bit1));
66 BOOST_TEST(multipleFlags.test(TestEnum::Bit3));
67 BOOST_TEST(!multipleFlags.test(TestEnum::Bit2));
68 BOOST_TEST(multipleFlags.any());
69
70 // Test reset
71 multipleFlags.reset(TestEnum::Bit1);
72 BOOST_TEST(!multipleFlags.test(TestEnum::Bit1));
73 BOOST_TEST(multipleFlags.test(TestEnum::Bit3));
74 multipleFlags.reset();
75 BOOST_TEST(!multipleFlags.any());
76
77 // Test multiset
78 multipleFlags.reset();
79 multipleFlags.set(TestEnum::Bit2, TestEnum::Bit4);
80 BOOST_TEST(!multipleFlags.test(TestEnum::Bit1));
81 BOOST_TEST(multipleFlags.test(TestEnum::Bit2));
82 BOOST_TEST(!multipleFlags.test(TestEnum::Bit3));
83 BOOST_TEST(multipleFlags.test(TestEnum::Bit4));
84 BOOST_TEST(!multipleFlags.test(TestEnum::Bit5VeryLongName));
85
86 // Test operator|
87 EFlags combinedFlags = flag1 | EFlags(TestEnum::Bit2);
88 BOOST_TEST(combinedFlags.test(TestEnum::Bit1));
89 BOOST_TEST(combinedFlags.test(TestEnum::Bit2));
90 BOOST_TEST(!combinedFlags.test(TestEnum::Bit3));
91 combinedFlags |= TestEnum::Bit5VeryLongName;
92 BOOST_TEST(combinedFlags.test(TestEnum::Bit5VeryLongName));
93
94 // Test operator[]
95 BOOST_TEST(combinedFlags[TestEnum::Bit1]);
96 BOOST_TEST(combinedFlags[TestEnum::Bit2]);
97 BOOST_TEST(!combinedFlags[TestEnum::Bit3]);
98
99 // Test operator|=
100 combinedFlags |= TestEnum::Bit3;
101 BOOST_TEST(combinedFlags.test(TestEnum::Bit3));
102
103 // Test operator&
104 EFlags intersection = combinedFlags & TestEnum::Bit1;
105 BOOST_TEST(intersection.test(TestEnum::Bit1));
106 BOOST_TEST(!intersection.test(TestEnum::Bit2));
107 BOOST_TEST(intersection.value() == (1 << static_cast<unsigned int>(TestEnum::Bit1)));
108
109 // Test operator&=
110 combinedFlags &= TestEnum::Bit1;
111 BOOST_TEST(combinedFlags.test(TestEnum::Bit1));
112 BOOST_TEST(!combinedFlags.test(TestEnum::Bit2));
113 BOOST_TEST(!combinedFlags.test(TestEnum::Bit3));
114
115 // Test operator~ (complement)
116 EFlags complement = ~EFlags(TestEnum::Bit1);
117 BOOST_TEST(!complement.test(TestEnum::Bit1));
118 BOOST_TEST(complement.test(TestEnum::Bit2));
119 BOOST_TEST(complement.test(TestEnum::Bit3));
120
121 // Test string() method
122 {
123 std::string flagString = flag1.string();
124 BOOST_TEST(flagString.back() == '1'); // Ensure the least significant bit is set for flag1
125 }
126
127 // Test set with binary string
128 {
129 std::string binaryStr = "101";
130 flags.set(binaryStr, 2);
134 }
135
136 // Test invalid binary string in set
137 BOOST_CHECK_THROW(flags.set(std::string("invalid"), 2), std::invalid_argument);
138
139 // Test range validation in set
140 BOOST_CHECK_THROW(flags.set(std::string("100000000"), 2), std::out_of_range);
141
142 { // Test that return lists are sensible
143 const auto n = flags.getNames();
144 const auto v = flags.getValues();
145 BOOST_CHECK(n.size() == v.size());
146 }
147
148 { // print test
149 std::cout << flags;
150 }
151
152 // Test flag tokenization and parsing
153 {
154 { // only one scoped flag
155 std::string str = "TestEnum::Bit2";
156 flags.set(str);
159 }
160
161 { // test with ws-triming and scope mixing
162 std::string str = "Bit4|TestEnum::Bit2 | Bit1 ";
163 flags.set(str);
168 }
169
170 { // test with , delimiter
171 std::string str = "Bit4,TestEnum::Bit2 , Bit1 ";
172 flags.set(str);
177 }
178
179 { // test with ; delimiter
180 std::string str = "Bit4;TestEnum::Bit2 ; Bit1 ";
181 flags.set(str);
186 }
187
188 { // throw test with mixed delimiter
189 std::string str = "Bit4|TestEnum::Bit2 , Bit1 ";
190 BOOST_CHECK_THROW(flags.set(str), std::invalid_argument);
191 }
192
193 { // test throw
194 std::string str = "Invalid";
195 BOOST_CHECK_THROW(flags.set(str), std::invalid_argument);
196 }
197 }
198
199 // Test all_of and none_of
200 {
201 EFlags allFlags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3});
202 BOOST_TEST(allFlags.all_of(TestEnum::Bit1, TestEnum::Bit2));
203 BOOST_TEST(!allFlags.all_of(TestEnum::Bit4));
204 BOOST_TEST(allFlags.none_of(TestEnum::Bit4));
205 }
206
207 // Test toggle
208 {
209 EFlags toggleFlags;
210 toggleFlags.toggle(TestEnum::Bit4);
211 BOOST_TEST(toggleFlags.test(TestEnum::Bit4));
212 toggleFlags.toggle(TestEnum::Bit4);
213 BOOST_TEST(!toggleFlags.test(TestEnum::Bit4));
214 }
215
216 // Create a flag set and serialize it
217 {
218 EFlags serializedFlags{TestEnum::Bit1, TestEnum::Bit3};
219 std::string serialized = serializedFlags.serialize();
220 BOOST_CHECK_EQUAL(serialized, "5"); // 5 in binary is 0101, meaning Bit1 and Bit3 are set.
221
222 // Deserialize back into a flag set
223 EFlags deserializedFlags;
224 deserializedFlags.deserialize(serialized);
225 BOOST_CHECK(deserializedFlags == serializedFlags); // Ensure the deserialized flags match the original
226 }
227
228 // Test with an empty flag set
229 {
230 EFlags emptyFlags;
231 std::string serialized = emptyFlags.serialize();
232 BOOST_CHECK_EQUAL(serialized, "0");
233
234 EFlags deserialized;
235 deserialized.deserialize(serialized);
236 BOOST_CHECK(deserialized == emptyFlags);
237
238 // Test with all flags set
239 EFlags allFlags(EFlags::All);
240 serialized = allFlags.serialize();
241 BOOST_CHECK_EQUAL(serialized, std::to_string(EFlags::All));
242
243 deserialized.deserialize(serialized);
244 BOOST_CHECK(deserialized == allFlags);
245 }
246
247 // check throw deserializng out of range
248 {
249 EFlags flag;
250 std::string str = "999999";
251 BOOST_CHECK_THROW(flag.deserialize(str), std::out_of_range);
252 }
253
254 // Create two flag sets
255 {
256 EFlags flags1{TestEnum::Bit1, TestEnum::Bit2};
257 EFlags flags2{TestEnum::Bit3, TestEnum::Bit4};
258
259 // Perform a union operation
260 EFlags unionFlags = flags1.union_with(flags2);
261 BOOST_CHECK(unionFlags.test(TestEnum::Bit1));
262 BOOST_CHECK(unionFlags.test(TestEnum::Bit2));
263 BOOST_CHECK(unionFlags.test(TestEnum::Bit3));
264 BOOST_CHECK(unionFlags.test(TestEnum::Bit4));
265 BOOST_CHECK_EQUAL(unionFlags.value(), 15); // 1111 in binary
266 }
267
268 // Create two overlapping flag sets
269 {
272
273 // test xor
274 auto flagsXOR = flags3 ^ flags4;
276
277 // test and
278 auto flagsAND = flags3 & flags4;
280
281 // Perform an intersection operation
282 EFlags intersectionFlags = flags3.intersection_with(flags4);
283 BOOST_CHECK(intersectionFlags.test(TestEnum::Bit2));
284 BOOST_CHECK(intersectionFlags.test(TestEnum::Bit3));
285 BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit1));
286 BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit4));
287 BOOST_CHECK_EQUAL(intersectionFlags.value(), 6); // 0110 in binary
288 }
289
290 {
291 // Check special flag names.
292 EFlags flag("all");
293 BOOST_CHECK(flag.all());
294 flag.set("none");
295 BOOST_CHECK(!flag.any());
296 }
297
298 {
299 // Create two flag sets
301 EFlags flags2{TestEnum::Bit2, TestEnum::Bit3};
302
303 // Check containment
304 BOOST_CHECK(flags1.contains(flags2)); // flags1 contains all flags in flags2
305 BOOST_CHECK(!flags2.contains(flags1)); // flags2 does not contain all flags in flags1
306
307 // Test with disjoint sets
308 EFlags flags3{TestEnum::Bit4};
309 BOOST_CHECK(!flags1.contains(flags3)); // flags1 does not contain flags3
310 }
311
312 {
313 // Test compilation using an enum with more than 32 bits
314 // Also tests space delimiter and construction from string.
316 BOOST_CHECK(test.test(TestEnumLong::Bit32, TestEnumLong::Bit34));
317 BOOST_CHECK(!test.test(TestEnumLong::Bit1, TestEnumLong::Bit23));
318 }
319}
320
321BOOST_AUTO_TEST_CASE(Flags_case_insensitive_test)
322{
323 using EFlags = o2::utils::EnumFlags<TestEnum>;
324
325 // Test case-insensitive flag names
326 {
327 EFlags flags("bit1"); // lowercase
330 }
331
332 {
333 EFlags flags("BIT2"); // uppercase
336 }
337
338 {
339 EFlags flags("BiT3"); // mixed case
341 }
342
343 {
344 EFlags flags("bit1|BIT2|BiT3"); // mixed case with delimiter
348 }
349
350 // Test special keywords case-insensitive
351 {
352 EFlags flags("ALL");
353 BOOST_CHECK(flags.all());
354 }
355
356 {
357 EFlags flags("None");
358 BOOST_CHECK(!flags.any());
359 }
360}
361
362BOOST_AUTO_TEST_CASE(Flags_error_recovery_test)
363{
364 using EFlags = o2::utils::EnumFlags<TestEnum>;
365
366 // Test that previous state is restored on exception
367 {
369 auto previousValue = flags.value();
370
371 // Try to set with invalid string
372 BOOST_CHECK_THROW(flags.set("InvalidFlag"), std::invalid_argument);
373
374 // Verify state was restored
375 BOOST_CHECK_EQUAL(flags.value(), previousValue);
378 }
379
380 {
382 auto previousValue = flags.value();
383
384 // Try to set with out-of-range value
385 BOOST_CHECK_THROW(flags.set("999999", 10), std::out_of_range);
386
387 // Verify state was restored
388 BOOST_CHECK_EQUAL(flags.value(), previousValue);
391 }
392
393 {
395 auto previousValue = flags.value();
396
397 // Try to set with invalid binary string
398 BOOST_CHECK_THROW(flags.set("10102", 2), std::invalid_argument);
399
400 // Verify state was restored
401 BOOST_CHECK_EQUAL(flags.value(), previousValue);
403 }
404}
405
406BOOST_AUTO_TEST_CASE(Flags_whitespace_handling_test)
407{
408 using EFlags = o2::utils::EnumFlags<TestEnum>;
409
410 // Test leading/trailing whitespace
411 {
412 EFlags flags(" Bit1 ");
414 }
415
416 {
417 EFlags flags(" Bit1 | Bit2 ");
420 }
421
422 // Test excessive whitespace between flags
423 {
424 EFlags flags("Bit1 | Bit3");
428 }
429
430 // Test tabs and other whitespace (should work with space delimiter)
431 {
432 EFlags flags("Bit1 Bit2 Bit3");
436 }
437}
438
439BOOST_AUTO_TEST_CASE(Flags_count_bits_test)
440{
441 using EFlags = o2::utils::EnumFlags<TestEnum>;
442
443 // Test counting set bits
444 {
445 EFlags flags;
446 BOOST_CHECK_EQUAL(flags.count(), 0);
447 }
448
449 {
450 EFlags flags(TestEnum::Bit1);
451 BOOST_CHECK_EQUAL(flags.count(), 1);
452 }
453
454 {
456 BOOST_CHECK_EQUAL(flags.count(), 2);
457 }
458
459 {
461 BOOST_CHECK_EQUAL(flags.count(), 4);
462 }
463
464 {
465 EFlags flags(EFlags::All);
466 BOOST_CHECK_EQUAL(flags.count(), 5); // TestEnum has 5 members
467 }
468
469 // Test count after operations
470 {
472 BOOST_CHECK_EQUAL(flags.count(), 3);
473
474 flags.reset(TestEnum::Bit2);
475 BOOST_CHECK_EQUAL(flags.count(), 2);
476
478 BOOST_CHECK_EQUAL(flags.count(), 3);
479
480 flags.toggle(TestEnum::Bit1);
481 BOOST_CHECK_EQUAL(flags.count(), 2);
482 }
483}
484
485BOOST_AUTO_TEST_CASE(Flags_mixed_delimiter_validation_test)
486{
487 using EFlags = o2::utils::EnumFlags<TestEnum>;
488
489 // Test that mixed delimiters throw an error
490 {
491 BOOST_CHECK_THROW(EFlags("Bit1|Bit2,Bit3"), std::invalid_argument);
492 }
493
494 {
495 BOOST_CHECK_THROW(EFlags("Bit1;Bit2|Bit3"), std::invalid_argument);
496 }
497
498 {
499 BOOST_CHECK_THROW(EFlags("Bit1,Bit2;Bit3"), std::invalid_argument);
500 }
501
502 {
503 BOOST_CHECK_THROW(EFlags("Bit1|Bit2,Bit3;Bit4"), std::invalid_argument);
504 }
505
506 // Test that single delimiter types work
507 {
508 EFlags flags1("Bit1|Bit2|Bit3");
509 BOOST_CHECK_EQUAL(flags1.count(), 3);
510 }
511
512 {
513 EFlags flags2("Bit1,Bit2,Bit3");
514 BOOST_CHECK_EQUAL(flags2.count(), 3);
515 }
516
517 {
518 EFlags flags3("Bit1;Bit2;Bit3");
519 BOOST_CHECK_EQUAL(flags3.count(), 3);
520 }
521}
522
523BOOST_AUTO_TEST_CASE(Flags_empty_and_edge_cases_test)
524{
525 using EFlags = o2::utils::EnumFlags<TestEnum>;
526
527 // Test empty string
528 {
530 flags.set(""); // Should be no-op
533 }
534
535 // Test with only whitespace
536 {
537 EFlags flags({TestEnum::Bit1});
538 flags.set(" "); // Should result in empty after tokenization
539 // Depending on implementation, this might clear or throw
540 // Adjust expectation based on actual behavior
541 }
542
543 // Test duplicate flags (should work, setting same bit twice is idempotent)
544 {
545 EFlags flags("Bit1|Bit1|Bit1");
547 BOOST_CHECK_EQUAL(flags.count(), 1);
548 }
549
550 // Test scoped and unscoped mixed
551 {
552 EFlags flags("Bit1|TestEnum::Bit2");
555 }
556}
557
558BOOST_AUTO_TEST_CASE(Flags_binary_decimal_parsing_test)
559{
560 using EFlags = o2::utils::EnumFlags<TestEnum>;
561
562 // Test binary parsing
563 {
564 EFlags flags("101", 2);
565 BOOST_CHECK(flags.test(TestEnum::Bit1)); // bit 0
566 BOOST_CHECK(!flags.test(TestEnum::Bit2)); // bit 1
567 BOOST_CHECK(flags.test(TestEnum::Bit3)); // bit 2
568 }
569
570 // Test decimal parsing
571 {
572 EFlags flags("7", 10); // 7 = 0b111
577 }
578
579 // Test hexadecimal parsing
580 {
581 EFlags flags("F", 16); // 15 = 0b1111
587 }
588
589 // Test hexadecimal with 0x prefix
590 {
591 EFlags flags("0xA", 16); // 10 = 0b1010
596 }
597
598 // Test hexadecimal with 0X prefix (uppercase)
599 {
600 EFlags flags("0X1F", 16); // 31 = all 5 bits
601 BOOST_CHECK(flags.all());
602 }
603
604 // Test lowercase hex digits
605 {
606 EFlags flags("0xa", 16);
607 BOOST_CHECK_EQUAL(flags.value(), 10);
608 }
609
610 // Test thros
611 {
612 BOOST_CHECK_THROW(EFlags("0xAbCd", 16), std::out_of_range);
613 }
614
615 // Test invalid binary string (contains 2)
616 {
617 BOOST_CHECK_THROW(EFlags("1012", 2), std::invalid_argument);
618 }
619
620 // Test out of range for base
621 {
622 BOOST_CHECK_THROW(EFlags("100000", 2), std::out_of_range);
623 }
624}
625
626BOOST_AUTO_TEST_CASE(Flags_operator_bool_test)
627{
628 using EFlags = o2::utils::EnumFlags<TestEnum>;
629
630 // Test explicit bool conversion
631 {
632 EFlags empty;
633 BOOST_CHECK(!static_cast<bool>(empty));
634 }
635
636 {
637 EFlags withFlag(TestEnum::Bit1);
638 BOOST_CHECK(static_cast<bool>(withFlag));
639 }
640
641 // Test in conditional
642 {
643 EFlags flags;
644 if (flags) {
645 BOOST_FAIL("Empty flags should be false");
646 }
647
649 if (!flags) {
650 BOOST_FAIL("Non-empty flags should be true");
651 }
652 }
653}
Class to aggregate and manage enum-based on-off flags.
Definition EnumFlags.h:359
constexpr auto value() const noexcept
Definition EnumFlags.h:436
void set(const std::string &s, int base=DefaultBase)
Definition EnumFlags.h:420
GLdouble n
Definition glcorearb.h:1982
const GLdouble * v
Definition glcorearb.h:832
GLbitfield flags
Definition glcorearb.h:1570
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
FIXME: do not use data model tables.
void empty(int)
#define ENUM_BIT(z, n, _)
TestEnum
@ Bit5VeryLongName
TestEnumLong
BOOST_AUTO_TEST_CASE(Flags_test)
BOOST_CHECK(tree)
BOOST_TEST(digits==digitsD, boost::test_tools::per_element())
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())
const std::string str