| 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 |  |