| 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 | |
| 30 | class tst_QFlags: public QObject |
| 31 | { |
| 32 | Q_OBJECT |
| 33 | private 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 | |
| 47 | void 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 | |
| 90 | void 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 | |
| 114 | void 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 | |
| 125 | void 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 | |
| 145 | void 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 | |
| 161 | template <unsigned int N, typename T> bool verifyConstExpr(T n) { return n == N; } |
| 162 | |
| 163 | Q_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 | |
| 173 | void 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 | |
| 201 | void 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 | |
| 214 | enum class MyStrictEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 }; |
| 215 | Q_DECLARE_FLAGS( MyStrictFlags, MyStrictEnum ) |
| 216 | Q_DECLARE_OPERATORS_FOR_FLAGS( MyStrictFlags ) |
| 217 | |
| 218 | enum class MyStrictNoOpEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 }; |
| 219 | Q_DECLARE_FLAGS( MyStrictNoOpFlags, MyStrictNoOpEnum ) |
| 220 | |
| 221 | Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isComplex ); |
| 222 | Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isStatic ); |
| 223 | Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isLarge ); |
| 224 | Q_STATIC_ASSERT( !QTypeInfo<MyStrictFlags>::isPointer ); |
| 225 | |
| 226 | void 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 | |
| 328 | void 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 | |
| 341 | void 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 | |
| 366 | namespace SomeNS { |
| 367 | enum Foo { Foo_A = 1 << 0, Foo_B = 1 << 1, Foo_C = 1 << 2 }; |
| 368 | |
| 369 | Q_DECLARE_FLAGS(Foos, Foo) |
| 370 | Q_DECLARE_OPERATORS_FOR_FLAGS(Foos); |
| 371 | |
| 372 | Qt::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 | |
| 379 | void 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: |
| 388 | enum MyEnum { Zero, One, Two, Four=4 }; |
| 389 | Q_DECLARE_FLAGS( MyFlags, MyEnum ) |
| 390 | Q_DECLARE_OPERATORS_FOR_FLAGS( MyFlags ) |
| 391 | |
| 392 | Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isComplex ); |
| 393 | Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isStatic ); |
| 394 | Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isLarge ); |
| 395 | Q_STATIC_ASSERT( !QTypeInfo<MyFlags>::isPointer ); |
| 396 | |
| 397 | QTEST_MAIN(tst_QFlags) |
| 398 | #include "tst_qflags.moc" |
| 399 | |