1 // Copyright (C) 2016-2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
2 // This file is subject to the license terms in the LICENSE file
3 // found in the top-level directory of this distribution.
4 
5 #include <type_safe/flag_set.hpp>
6 
7 #include <catch.hpp>
8 
9 // no using namespace to test operator namespace
10 
11 enum class test_flags
12 {
13     a,
14     b,
15     c
16 };
17 
18 namespace type_safe
19 {
20 template <>
21 struct flag_set_traits<test_flags> : std::true_type
22 {
sizetype_safe::flag_set_traits23     static constexpr std::size_t size()
24     {
25         return 3;
26     }
27 };
28 } // namespace type_safe
29 
check_set(const type_safe::flag_set<test_flags> & set,bool a,bool b,bool c)30 void check_set(const type_safe::flag_set<test_flags>& set, bool a, bool b, bool c)
31 {
32     REQUIRE(set.is_set(test_flags::a) == a);
33     REQUIRE(set.as_flag(test_flags::a) == a);
34     if (a)
35         REQUIRE((set & test_flags::a));
36 
37     REQUIRE(set.is_set(test_flags::b) == b);
38     REQUIRE(set.as_flag(test_flags::b) == b);
39     if (b)
40         REQUIRE((set & test_flags::b));
41 
42     REQUIRE(set.is_set(test_flags::c) == c);
43     REQUIRE(set.as_flag(test_flags::c) == c);
44     if (c)
45         REQUIRE((set & test_flags::c));
46 
47     if (a || b || c)
48     {
49         REQUIRE(set.any());
50         REQUIRE_FALSE(set.none());
51         REQUIRE(set != type_safe::noflag);
52         REQUIRE(type_safe::noflag != set);
53     }
54     else
55     {
56         REQUIRE_FALSE(set.any());
57         REQUIRE(set.none());
58         REQUIRE(set == type_safe::noflag);
59         REQUIRE(type_safe::noflag == set);
60     }
61 
62     auto number = (a ? 4 : 0) + (b ? 2 : 0) + (c ? 1 : 0);
63     switch (number)
64     {
65     case 0:
66         REQUIRE(set == type_safe::flag_set<test_flags>());
67         break;
68     case 1:
69         REQUIRE(set == test_flags::c);
70         REQUIRE(set == type_safe::combo(~test_flags::a & ~test_flags::b));
71         REQUIRE((set & test_flags::c));
72         break;
73     case 2:
74         REQUIRE(set == test_flags::b);
75         REQUIRE(set == type_safe::combo(~test_flags::a & ~test_flags::c));
76         REQUIRE((set & test_flags::b));
77         break;
78     case 3:
79         REQUIRE(set == (test_flags::b | test_flags::c));
80         REQUIRE(set == type_safe::combo(~test_flags::a));
81         REQUIRE((set & (test_flags::b | test_flags::c)));
82         break;
83     case 4:
84         REQUIRE(set == test_flags::a);
85         REQUIRE(set == type_safe::combo(~test_flags::b & ~test_flags::c));
86         REQUIRE((set & test_flags::a));
87         break;
88     case 5:
89         REQUIRE(set == (test_flags::a | test_flags::c));
90         REQUIRE(set == type_safe::combo(~test_flags::b));
91         REQUIRE((set & (test_flags::a | test_flags::c)));
92         break;
93     case 6:
94         REQUIRE(set == (test_flags::a | test_flags::b));
95         REQUIRE(set == type_safe::combo(~test_flags::c));
96         REQUIRE((set & (test_flags::a | test_flags::b)));
97         break;
98     case 7:
99         REQUIRE(set.all());
100         REQUIRE(set == (test_flags::a | test_flags::b | test_flags::c));
101         REQUIRE((set & (test_flags::a | test_flags::b | test_flags::c)));
102         break;
103 
104     default:
105         REQUIRE(false);
106         break;
107     }
108 }
109 
110 TEST_CASE("flag_set_traits")
111 {
112     using namespace type_safe;
113 
114     enum class a
115     {
116         e1,
117         e2,
118         e3
119     };
120     static_assert(!flag_set_traits<a>::value, "a is not a flag set enum");
121 
122     enum class b
123     {
124         e1,
125         e2,
126         e3,
127         _flag_set_size,
128     };
129     static_assert(flag_set_traits<b>::value, "b is a flag set enum");
130     static_assert(flag_set_traits<b>::size() == 3u, "size of b is 3");
131 }
132 
133 TEST_CASE("flag_set")
134 {
135     using namespace type_safe;
136 
137     using set = flag_set<test_flags>;
138 
139     set s;
140     check_set(s, false, false, false);
141 
142     SECTION("constructor/assignment")
143     {
144         set a;
145         check_set(a, false, false, false);
146 
147         set b(test_flags::a);
148         check_set(b, true, false, false);
149 
150         a = test_flags::b;
151         check_set(a, false, true, false);
152 
153         b = test_flags::c;
154         check_set(b, false, false, true);
155     }
156     SECTION("set")
157     {
158         s.set(test_flags::a);
159         check_set(s, true, false, false);
160 
161         s.set(test_flags::b, true);
162         check_set(s, true, true, false);
163 
164         s.set(test_flags::a, false);
165         check_set(s, false, true, false);
166 
167         s.set(test_flags::a, flag(true));
168         check_set(s, true, true, false);
169     }
170     SECTION("reset")
171     {
172         s.set(test_flags::a);
173         check_set(s, true, false, false);
174 
175         s.reset(test_flags::b);
176         check_set(s, true, false, false);
177 
178         s.reset(test_flags::a);
179         check_set(s, false, false, false);
180     }
181     SECTION("toggle")
182     {
183         s.toggle(test_flags::a);
184         check_set(s, true, false, false);
185 
186         s.toggle(test_flags::b);
187         check_set(s, true, true, false);
188 
189         s.toggle(test_flags::a);
190         check_set(s, false, true, false);
191     }
192     SECTION("set_all/reset_all/toggle_all")
193     {
194         s.set_all();
195         check_set(s, true, true, true);
196 
197         s.reset_all();
198         check_set(s, false, false, false);
199 
200         s.set(test_flags::c);
201         check_set(s, false, false, true);
202 
203         s.toggle_all();
204         check_set(s, true, true, false);
205     }
206     SECTION("binary op")
207     {
208         s |= test_flags::a;
209         check_set(s, true, false, false);
210 
211         s |= test_flags::b | test_flags::c;
212         check_set(s, true, true, true);
213 
214         s &= ~test_flags::c;
215         check_set(s, true, true, false);
216 
217         s = ~s;
218         check_set(s, false, false, true);
219 
220         s ^= test_flags::a | test_flags::c;
221         check_set(s, true, false, false);
222     }
223 }
224