1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <QtTest/QtTest>
29
30class tst_QFlags: public QObject
31{
32 Q_OBJECT
33private slots:
34 void boolCasts() const;
35 void operators() const;
36 void testFlag() const;
37 void testFlagZeroFlag() const;
38 void testFlagMultiBits() const;
39 void constExpr();
40 void signedness();
41 void classEnum();
42 void initializerLists();
43 void testSetFlags();
44 void adl();
45};
46
47void tst_QFlags::boolCasts() const
48{
49 // This tests that the operator overloading is sufficient so that common
50 // idioms involving flags -> bool casts work as expected:
51
52 const Qt::Alignment nonNull = Qt::AlignCenter;
53 const Qt::Alignment null = {};
54
55 // basic premiss:
56 QVERIFY(bool(nonNull));
57 QVERIFY(!bool(null));
58
59 // The rest is just checking that stuff compiles:
60
61 // QVERIFY should compile:
62 QVERIFY(nonNull);
63 QVERIFY(!null);
64
65 // ifs should compile:
66 if (null) QFAIL("Can't contextually convert QFlags to bool!");
67 if (!nonNull) QFAIL("Missing operator! on QFlags (shouldn't be necessary).");
68
69 // ternary should compile:
70 QVERIFY(nonNull ? true : false);
71 QVERIFY(!null ? true : false);
72
73 // logical operators should compile:
74 QVERIFY(nonNull && true);
75 QVERIFY(nonNull || false);
76 QVERIFY(!null && true);
77 QVERIFY(!null || false);
78
79 // ... in both directions:
80 QVERIFY(true && nonNull);
81 QVERIFY(false || nonNull);
82 QVERIFY(true && !null);
83 QVERIFY(false || !null);
84
85 // ... and mixed:
86 QVERIFY(null || nonNull);
87 QVERIFY(!(null && nonNull));
88}
89
90void tst_QFlags::operators() const
91{
92#define CHECK(op, LHS, RHS, RES) \
93 do { \
94 using LFlags = QFlags<decltype(LHS)>; \
95 using RFlags = QFlags<decltype(RHS)>; \
96 QCOMPARE((LHS op RHS), (RES)); \
97 QCOMPARE((LFlags(LHS) op RHS), (RES)); \
98 QCOMPARE((LHS op RFlags(RHS)), (RES)); \
99 QCOMPARE((LFlags(LHS) op RFlags(RHS)), (RES)); \
100 QCOMPARE((LFlags(LHS) op ## = RHS), (RES)); \
101 QCOMPARE((LFlags(LHS) op ## = RFlags(RHS)), (RES)); \
102 } while (false)
103
104 CHECK(|, Qt::AlignHCenter, Qt::AlignVCenter, Qt::AlignCenter);
105 CHECK(|, Qt::AlignHCenter, Qt::AlignHCenter, Qt::AlignHCenter);
106 CHECK(&, Qt::AlignHCenter, Qt::AlignVCenter, Qt::Alignment());
107 CHECK(&, Qt::AlignHCenter, Qt::AlignHCenter, Qt::AlignHCenter);
108 CHECK(^, Qt::AlignHCenter, Qt::AlignVCenter, Qt::AlignCenter);
109 CHECK(^, Qt::AlignHCenter, Qt::AlignHCenter, Qt::Alignment());
110
111#undef CHECK
112}
113
114void tst_QFlags::testFlag() const
115{
116 Qt::MouseButtons btn = Qt::LeftButton | Qt::RightButton;
117
118 QVERIFY(btn.testFlag(Qt::LeftButton));
119 QVERIFY(!btn.testFlag(Qt::MiddleButton));
120
121 btn = { };
122 QVERIFY(!btn.testFlag(Qt::LeftButton));
123}
124
125void tst_QFlags::testFlagZeroFlag() const
126{
127 {
128 Qt::MouseButtons btn = Qt::LeftButton | Qt::RightButton;
129 /* Qt::NoButton has the value 0. */
130
131 QVERIFY(!btn.testFlag(Qt::NoButton));
132 }
133
134 {
135 /* A zero enum set should test true with zero. */
136 QVERIFY(Qt::MouseButtons().testFlag(Qt::NoButton));
137 }
138
139 {
140 Qt::MouseButtons btn = Qt::NoButton;
141 QVERIFY(btn.testFlag(Qt::NoButton));
142 }
143}
144
145void tst_QFlags::testFlagMultiBits() const
146{
147 /* Qt::Window is 0x00000001
148 * Qt::Dialog is 0x00000002 | Window
149 */
150 {
151 const Qt::WindowFlags onlyWindow(Qt::Window);
152 QVERIFY(!onlyWindow.testFlag(Qt::Dialog));
153 }
154
155 {
156 const Qt::WindowFlags hasDialog(Qt::Dialog);
157 QVERIFY(hasDialog.testFlag(Qt::Dialog));
158 }
159}
160
161template <unsigned int N, typename T> bool verifyConstExpr(T n) { return n == N; }
162
163Q_DECL_RELAXED_CONSTEXPR Qt::MouseButtons testRelaxedConstExpr()
164{
165 Qt::MouseButtons value;
166 value = Qt::LeftButton | Qt::RightButton;
167 value |= Qt::MiddleButton;
168 value &= ~Qt::LeftButton;
169 value ^= Qt::RightButton;
170 return value;
171}
172
173void tst_QFlags::constExpr()
174{
175#ifdef Q_COMPILER_CONSTEXPR
176 Qt::MouseButtons btn = Qt::LeftButton | Qt::RightButton;
177 switch (btn) {
178 case Qt::LeftButton: QVERIFY(false); break;
179 case Qt::RightButton: QVERIFY(false); break;
180 case int(Qt::LeftButton | Qt::RightButton): QVERIFY(true); break;
181 default: QVERIFY(false);
182 }
183
184 QVERIFY(verifyConstExpr<uint((Qt::LeftButton | Qt::RightButton) & Qt::LeftButton)>(Qt::LeftButton));
185 QVERIFY(verifyConstExpr<uint((Qt::LeftButton | Qt::RightButton) & Qt::MiddleButton)>(0));
186 QVERIFY(verifyConstExpr<uint((Qt::LeftButton | Qt::RightButton) | Qt::MiddleButton)>(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton));
187 QVERIFY(verifyConstExpr<uint(~(Qt::LeftButton | Qt::RightButton))>(~(Qt::LeftButton | Qt::RightButton)));
188 QVERIFY(verifyConstExpr<uint(Qt::MouseButtons(Qt::LeftButton) ^ Qt::RightButton)>(Qt::LeftButton ^ Qt::RightButton));
189 QVERIFY(verifyConstExpr<uint(Qt::MouseButtons(0))>(0));
190 QVERIFY(verifyConstExpr<uint(Qt::MouseButtons(Qt::RightButton) & 0xff)>(Qt::RightButton));
191 QVERIFY(verifyConstExpr<uint(Qt::MouseButtons(Qt::RightButton) | 0xff)>(0xff));
192
193 QVERIFY(!verifyConstExpr<Qt::RightButton>(~Qt::MouseButtons(Qt::LeftButton)));
194
195#if defined(__cpp_constexpr) && __cpp_constexpr-0 >= 201304
196 QVERIFY(verifyConstExpr<uint(testRelaxedConstExpr())>(Qt::MiddleButton));
197#endif
198#endif
199}
200
201void tst_QFlags::signedness()
202{
203 // these are all 'true' on GCC, but since the std says the
204 // underlying type is implementation-defined, we need to allow for
205 // a different signedness, so we only check that the relative
206 // signedness of the types matches:
207 Q_STATIC_ASSERT((std::is_unsigned<typename std::underlying_type<Qt::MouseButton>::type>::value ==
208 std::is_unsigned<Qt::MouseButtons::Int>::value));
209
210 Q_STATIC_ASSERT((std::is_signed<typename std::underlying_type<Qt::AlignmentFlag>::type>::value ==
211 std::is_signed<Qt::Alignment::Int>::value));
212}
213
214enum class MyStrictEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 };
215Q_DECLARE_FLAGS( MyStrictFlags, MyStrictEnum )
216Q_DECLARE_OPERATORS_FOR_FLAGS( MyStrictFlags )
217
218enum class MyStrictNoOpEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 };
219Q_DECLARE_FLAGS( MyStrictNoOpFlags, MyStrictNoOpEnum )
220
221Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isComplex );
222Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isStatic );
223Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isLarge );
224Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isPointer );
225
226void tst_QFlags::classEnum()
227{
228 // The main aim of the test is making sure it compiles
229 // The QCOMPARE are there as an extra
230 MyStrictEnum e1 = MyStrictEnum::StrictOne;
231 MyStrictEnum e2 = MyStrictEnum::StrictTwo;
232
233 MyStrictFlags f1(MyStrictEnum::StrictOne);
234 QCOMPARE(f1, 1);
235
236 MyStrictFlags f2(e2);
237 QCOMPARE(f2, 2);
238
239 MyStrictFlags f0;
240 QCOMPARE(f0, 0);
241
242 MyStrictFlags f3(e2 | e1);
243 QCOMPARE(f3, 3);
244
245 QVERIFY(f3.testFlag(MyStrictEnum::StrictOne));
246 QVERIFY(!f1.testFlag(MyStrictEnum::StrictTwo));
247
248 QVERIFY(!f0);
249
250 QCOMPARE(f3 & int(1), 1);
251 QCOMPARE(f3 & uint(1), 1);
252 QCOMPARE(f3 & MyStrictEnum::StrictOne, 1);
253
254 MyStrictFlags aux;
255 aux = f3;
256 aux &= int(1);
257 QCOMPARE(aux, 1);
258
259 aux = f3;
260 aux &= uint(1);
261 QCOMPARE(aux, 1);
262
263 aux = f3;
264 aux &= MyStrictEnum::StrictOne;
265 QCOMPARE(aux, 1);
266
267 aux = f3;
268 aux &= f1;
269 QCOMPARE(aux, 1);
270
271 aux = f3 ^ f3;
272 QCOMPARE(aux, 0);
273
274 aux = f3 ^ f1;
275 QCOMPARE(aux, 2);
276
277 aux = f3 ^ f0;
278 QCOMPARE(aux, 3);
279
280 aux = f3 ^ MyStrictEnum::StrictOne;
281 QCOMPARE(aux, 2);
282
283 aux = f3 ^ MyStrictEnum::StrictZero;
284 QCOMPARE(aux, 3);
285
286 aux = f3;
287 aux ^= f3;
288 QCOMPARE(aux, 0);
289
290 aux = f3;
291 aux ^= f1;
292 QCOMPARE(aux, 2);
293
294 aux = f3;
295 aux ^= f0;
296 QCOMPARE(aux, 3);
297
298 aux = f3;
299 aux ^= MyStrictEnum::StrictOne;
300 QCOMPARE(aux, 2);
301
302 aux = f3;
303 aux ^= MyStrictEnum::StrictZero;
304 QCOMPARE(aux, 3);
305
306 aux = f1 | f2;
307 QCOMPARE(aux, 3);
308
309 aux = MyStrictEnum::StrictOne | MyStrictEnum::StrictTwo;
310 QCOMPARE(aux, 3);
311
312 aux = f1;
313 aux |= f2;
314 QCOMPARE(aux, 3);
315
316 aux = MyStrictEnum::StrictOne;
317 aux |= MyStrictEnum::StrictTwo;
318 QCOMPARE(aux, 3);
319
320 aux = ~f1;
321 QCOMPARE(aux, -2);
322
323 // Just to make sure it compiles
324 if (false)
325 qDebug() << f3;
326}
327
328void tst_QFlags::initializerLists()
329{
330 Qt::MouseButtons bts = { Qt::LeftButton, Qt::RightButton };
331 QVERIFY(bts.testFlag(Qt::LeftButton));
332 QVERIFY(bts.testFlag(Qt::RightButton));
333 QVERIFY(!bts.testFlag(Qt::MiddleButton));
334
335 MyStrictNoOpFlags flags = { MyStrictNoOpEnum::StrictOne, MyStrictNoOpEnum::StrictFour };
336 QVERIFY(flags.testFlag(MyStrictNoOpEnum::StrictOne));
337 QVERIFY(flags.testFlag(MyStrictNoOpEnum::StrictFour));
338 QVERIFY(!flags.testFlag(MyStrictNoOpEnum::StrictTwo));
339}
340
341void tst_QFlags::testSetFlags()
342{
343 Qt::MouseButtons btn = Qt::NoButton;
344
345 btn.setFlag(flag: Qt::LeftButton);
346 QVERIFY(btn.testFlag(Qt::LeftButton));
347 QVERIFY(!btn.testFlag(Qt::MiddleButton));
348
349 btn.setFlag(flag: Qt::LeftButton, on: false);
350 QVERIFY(!btn.testFlag(Qt::LeftButton));
351 QVERIFY(!btn.testFlag(Qt::MiddleButton));
352
353 MyStrictFlags flags;
354 flags.setFlag(flag: MyStrictEnum::StrictOne);
355 flags.setFlag(flag: MyStrictEnum::StrictTwo, on: true);
356 QVERIFY(flags.testFlag(MyStrictEnum::StrictOne));
357 QVERIFY(flags.testFlag(MyStrictEnum::StrictTwo));
358 QVERIFY(!flags.testFlag(MyStrictEnum::StrictFour));
359
360 flags.setFlag(flag: MyStrictEnum::StrictTwo, on: false);
361 QVERIFY(flags.testFlag(MyStrictEnum::StrictOne));
362 QVERIFY(!flags.testFlag(MyStrictEnum::StrictTwo));
363 QVERIFY(!flags.testFlag(MyStrictEnum::StrictFour));
364}
365
366namespace SomeNS {
367enum Foo { Foo_A = 1 << 0, Foo_B = 1 << 1, Foo_C = 1 << 2 };
368
369Q_DECLARE_FLAGS(Foos, Foo)
370Q_DECLARE_OPERATORS_FOR_FLAGS(Foos);
371
372Qt::Alignment alignment()
373{
374 // Checks that the operator| works, despite there is another operator| in this namespace.
375 return Qt::AlignLeft | Qt::AlignTop;
376}
377}
378
379void tst_QFlags::adl()
380{
381 SomeNS::Foos fl = SomeNS::Foo_B | SomeNS::Foo_C;
382 QVERIFY(fl & SomeNS::Foo_B);
383 QVERIFY(!(fl & SomeNS::Foo_A));
384 QCOMPARE(SomeNS::alignment(), Qt::AlignLeft | Qt::AlignTop);
385}
386
387// (statically) check QTypeInfo for QFlags instantiations:
388enum MyEnum { Zero, One, Two, Four=4 };
389Q_DECLARE_FLAGS( MyFlags, MyEnum )
390Q_DECLARE_OPERATORS_FOR_FLAGS( MyFlags )
391
392Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isComplex );
393Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isStatic );
394Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isLarge );
395Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isPointer );
396
397QTEST_MAIN(tst_QFlags)
398#include "tst_qflags.moc"
399

source code of qtbase/tests/auto/corelib/global/qflags/tst_qflags.cpp