| 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 |  | 
| 29 | #include <QtTest/QtTest> | 
| 30 |  | 
| 31 | #include <qhash.h> | 
| 32 |  | 
| 33 | #include <iterator> | 
| 34 | #include <sstream> | 
| 35 | #include <algorithm> | 
| 36 |  | 
| 37 | #include <unordered_set> | 
| 38 |  | 
| 39 | class tst_QHashFunctions : public QObject | 
| 40 | { | 
| 41 |     Q_OBJECT | 
| 42 | public: | 
| 43 |     enum { | 
| 44 |         // random value | 
| 45 |         RandomSeed = 1045982819 | 
| 46 |     }; | 
| 47 |     uint seed; | 
| 48 |  | 
| 49 | public slots: | 
| 50 |     void initTestCase(); | 
| 51 |     void init(); | 
| 52 |  | 
| 53 | private Q_SLOTS: | 
| 54 |     void consistent(); | 
| 55 |     void qhash(); | 
| 56 |     void qhash_of_empty_and_null_qstring(); | 
| 57 |     void qhash_of_empty_and_null_qbytearray(); | 
| 58 |     void fp_qhash_of_zero_is_seed(); | 
| 59 |     void qthash_data(); | 
| 60 |     void qthash(); | 
| 61 |     void range(); | 
| 62 |     void rangeCommutative(); | 
| 63 |  | 
| 64 |     void stdHash(); | 
| 65 |  | 
| 66 |     void setGlobalQHashSeed(); | 
| 67 | }; | 
| 68 |  | 
| 69 | void tst_QHashFunctions::consistent() | 
| 70 | { | 
| 71 |     // QString-like | 
| 72 |     { | 
| 73 |         const QString s = QStringLiteral("abcdefghijklmnopqrstuvxyz" ).repeated(times: 16); | 
| 74 |  | 
| 75 |         QCOMPARE(qHash(s), qHash(QStringRef(&s))); | 
| 76 |         QCOMPARE(qHash(s), qHash(QStringView(s))); | 
| 77 |     } | 
| 78 | } | 
| 79 |  | 
| 80 | void tst_QHashFunctions::initTestCase() | 
| 81 | { | 
| 82 |     Q_STATIC_ASSERT(int(RandomSeed) > 0); | 
| 83 |  | 
| 84 |     QTest::addColumn<uint>(name: "seedValue" ); | 
| 85 |     QTest::newRow(dataTag: "zero-seed" ) << 0U; | 
| 86 |     QTest::newRow(dataTag: "non-zero-seed" ) << uint(RandomSeed); | 
| 87 | } | 
| 88 |  | 
| 89 | void tst_QHashFunctions::init() | 
| 90 | { | 
| 91 |     QFETCH_GLOBAL(uint, seedValue); | 
| 92 |     seed = seedValue; | 
| 93 | } | 
| 94 |  | 
| 95 | void tst_QHashFunctions::qhash() | 
| 96 | { | 
| 97 |     { | 
| 98 |         QBitArray a1; | 
| 99 |         QBitArray a2; | 
| 100 |         QCOMPARE(qHash(a1, seed), seed); | 
| 101 |  | 
| 102 |         a1.resize(size: 1); | 
| 103 |         a1.setBit(i: 0, val: true); | 
| 104 |  | 
| 105 |         a2.resize(size: 1); | 
| 106 |         a2.setBit(i: 0, val: false); | 
| 107 |  | 
| 108 |         uint h1 = qHash(key: a1, seed); | 
| 109 |         uint h2 = qHash(key: a2, seed); | 
| 110 |  | 
| 111 |         QVERIFY(h1 != h2);  // not guaranteed | 
| 112 |  | 
| 113 |         a2.setBit(i: 0, val: true); | 
| 114 |         QVERIFY(h1 == qHash(a2, seed)); | 
| 115 |  | 
| 116 |         a1.fill(aval: true, asize: 8); | 
| 117 |         a1.resize(size: 7); | 
| 118 |  | 
| 119 |         h1 = qHash(key: a1, seed); | 
| 120 |  | 
| 121 |         a2.fill(aval: true, asize: 7); | 
| 122 |         h2 = qHash(key: a2, seed); | 
| 123 |  | 
| 124 |         QVERIFY(h1 == h2); | 
| 125 |  | 
| 126 |         a2.setBit(i: 0, val: false); | 
| 127 |         uint h3 = qHash(key: a2, seed); | 
| 128 |         QVERIFY(h2 != h3);  // not guaranteed | 
| 129 |  | 
| 130 |         a2.setBit(i: 0, val: true); | 
| 131 |         QVERIFY(h2 == qHash(a2, seed)); | 
| 132 |  | 
| 133 |         a2.setBit(i: 6, val: false); | 
| 134 |         uint h4 = qHash(key: a2, seed); | 
| 135 |         QVERIFY(h2 != h4);  // not guaranteed | 
| 136 |  | 
| 137 |         a2.setBit(i: 6, val: true); | 
| 138 |         QVERIFY(h2 == qHash(a2, seed)); | 
| 139 |  | 
| 140 |         QVERIFY(h3 != h4);  // not guaranteed | 
| 141 |     } | 
| 142 |  | 
| 143 |     { | 
| 144 |         QPair<int, int> p12(1, 2); | 
| 145 |         QPair<int, int> p21(2, 1); | 
| 146 |  | 
| 147 |         QVERIFY(qHash(p12, seed) == qHash(p12, seed)); | 
| 148 |         QVERIFY(qHash(p21, seed) == qHash(p21, seed)); | 
| 149 |         QVERIFY(qHash(p12, seed) != qHash(p21, seed));  // not guaranteed | 
| 150 |  | 
| 151 |         QPair<int, int> pA(0x12345678, 0x12345678); | 
| 152 |         QPair<int, int> pB(0x12345675, 0x12345675); | 
| 153 |  | 
| 154 |         QVERIFY(qHash(pA, seed) != qHash(pB, seed));    // not guaranteed | 
| 155 |     } | 
| 156 |  | 
| 157 |     { | 
| 158 |         std::pair<int, int> p12(1, 2); | 
| 159 |         std::pair<int, int> p21(2, 1); | 
| 160 |  | 
| 161 |         using QT_PREPEND_NAMESPACE(qHash); | 
| 162 |  | 
| 163 |         QVERIFY(qHash(p12, seed) == qHash(p12, seed)); | 
| 164 |         QVERIFY(qHash(p21, seed) == qHash(p21, seed)); | 
| 165 |         QVERIFY(qHash(p12, seed) != qHash(p21, seed));  // not guaranteed | 
| 166 |  | 
| 167 |         std::pair<int, int> pA(0x12345678, 0x12345678); | 
| 168 |         std::pair<int, int> pB(0x12345675, 0x12345675); | 
| 169 |  | 
| 170 |         QVERIFY(qHash(pA, seed) != qHash(pB, seed));    // not guaranteed | 
| 171 |     } | 
| 172 | } | 
| 173 |  | 
| 174 | void tst_QHashFunctions::qhash_of_empty_and_null_qstring() | 
| 175 | { | 
| 176 |     QString null, empty("" ); | 
| 177 |     QCOMPARE(null, empty); | 
| 178 |     QCOMPARE(qHash(null, seed), qHash(empty, seed)); | 
| 179 |  | 
| 180 |     QStringRef nullRef, emptyRef(&empty); | 
| 181 |     QCOMPARE(nullRef, emptyRef); | 
| 182 |     QCOMPARE(qHash(nullRef, seed), qHash(emptyRef, seed)); | 
| 183 |  | 
| 184 |     QStringView nullView, emptyView(empty); | 
| 185 |     QCOMPARE(nullView, emptyView); | 
| 186 |     QCOMPARE(qHash(nullView, seed), qHash(emptyView, seed)); | 
| 187 | } | 
| 188 |  | 
| 189 | void tst_QHashFunctions::qhash_of_empty_and_null_qbytearray() | 
| 190 | { | 
| 191 |     QByteArray null, empty("" ); | 
| 192 |     QCOMPARE(null, empty); | 
| 193 |     QCOMPARE(qHash(null, seed), qHash(empty, seed)); | 
| 194 | } | 
| 195 |  | 
| 196 | void tst_QHashFunctions::fp_qhash_of_zero_is_seed() | 
| 197 | { | 
| 198 |     QCOMPARE(qHash(-0.0f, seed), seed); | 
| 199 |     QCOMPARE(qHash( 0.0f, seed), seed); | 
| 200 |  | 
| 201 |     QCOMPARE(qHash(-0.0 , seed), seed); | 
| 202 |     QCOMPARE(qHash( 0.0 , seed), seed); | 
| 203 |  | 
| 204 | #ifndef Q_OS_DARWIN | 
| 205 |     QCOMPARE(qHash(-0.0L, seed), seed); | 
| 206 |     QCOMPARE(qHash( 0.0L, seed), seed); | 
| 207 | #endif | 
| 208 | } | 
| 209 |  | 
| 210 | void tst_QHashFunctions::qthash_data() | 
| 211 | { | 
| 212 |     QTest::addColumn<QString>(name: "key" ); | 
| 213 |     QTest::addColumn<uint>(name: "hash" ); | 
| 214 |  | 
| 215 |     QTest::newRow(dataTag: "null" ) << QString() << 0u; | 
| 216 |     QTest::newRow(dataTag: "empty" ) << QStringLiteral("" ) << 0u; | 
| 217 |     QTest::newRow(dataTag: "abcdef" ) << QStringLiteral("abcdef" ) << 108567222u; | 
| 218 |     QTest::newRow(dataTag: "tqbfjotld" ) << QStringLiteral("The quick brown fox jumps over the lazy dog" ) << 140865879u; | 
| 219 |     QTest::newRow(dataTag: "42" ) << QStringLiteral("42" ) << 882u; | 
| 220 | } | 
| 221 |  | 
| 222 | void tst_QHashFunctions::qthash() | 
| 223 | { | 
| 224 |     QFETCH(QString, key); | 
| 225 |     const uint result = qt_hash(key); | 
| 226 |     QTEST(result, "hash" ); | 
| 227 | } | 
| 228 |  | 
| 229 | namespace SomeNamespace { | 
| 230 |     struct Hashable { int i; }; | 
| 231 |     inline uint qHash(Hashable h, uint seed = 0) | 
| 232 |     { return QT_PREPEND_NAMESPACE(qHash)(key: h.i, seed); } | 
| 233 |  | 
| 234 |     struct AdlHashable { | 
| 235 |         int i; | 
| 236 |     private: | 
| 237 |         friend size_t qHash(AdlHashable h, size_t seed = 0) | 
| 238 |         { return QT_PREPEND_NAMESPACE(qHash)(key: h.i, seed); } | 
| 239 |     }; | 
| 240 | } | 
| 241 | void tst_QHashFunctions::range() | 
| 242 | { | 
| 243 |     static const int ints[] = {0, 1, 2, 3, 4, 5}; | 
| 244 |     static const size_t numInts = sizeof ints / sizeof *ints; | 
| 245 |  | 
| 246 |     // empty range just gives the seed: | 
| 247 |     QCOMPARE(qHashRange(ints, ints, seed), seed); | 
| 248 |     // verify that order matters (test not guaranteed): | 
| 249 |     QVERIFY(qHashRange(ints, ints + numInts, seed) != | 
| 250 |             qHashRange(std::reverse_iterator<const int*>(ints + numInts), std::reverse_iterator<const int*>(ints), seed)); | 
| 251 |  | 
| 252 |     { | 
| 253 |         // verify that the input iterator category suffices: | 
| 254 |         std::stringstream sstream; | 
| 255 |         Q_STATIC_ASSERT((std::is_same<std::input_iterator_tag, std::istream_iterator<int>::iterator_category>::value)); | 
| 256 |         std::copy(first: ints, last: ints + numInts, result: std::ostream_iterator<int>(sstream, " " )); | 
| 257 |         sstream.seekg(0); | 
| 258 |         std::istream_iterator<int> it(sstream), end; | 
| 259 |         QCOMPARE(qHashRange(ints, ints + numInts, seed), qHashRange(it, end, seed)); | 
| 260 |     } | 
| 261 |  | 
| 262 |     { | 
| 263 |         SomeNamespace::Hashable hashables[] = {{.i: 0}, {.i: 1}, {.i: 2}, {.i: 3}, {.i: 4}, {.i: 5}}; | 
| 264 |         // compile check: is qHash() found using ADL? | 
| 265 |         [[maybe_unused]] auto r = qHashRange(first: std::begin(arr&: hashables), last: std::end(arr&: hashables), seed); | 
| 266 |     } | 
| 267 |     { | 
| 268 |         SomeNamespace::AdlHashable hashables[] = {{.i: 0}, {.i: 1}, {.i: 2}, {.i: 3}, {.i: 4}, {.i: 5}}; | 
| 269 |         // compile check: is qHash() found as a hidden friend? | 
| 270 |         [[maybe_unused]] auto r = qHashRange(first: std::begin(arr&: hashables), last: std::end(arr&: hashables), seed); | 
| 271 |     } | 
| 272 | } | 
| 273 |  | 
| 274 | void tst_QHashFunctions::rangeCommutative() | 
| 275 | { | 
| 276 |     int ints[] = {0, 1, 2, 3, 4, 5}; | 
| 277 |     static const size_t numInts = sizeof ints / sizeof *ints; | 
| 278 |  | 
| 279 |     // empty range just gives the seed: | 
| 280 |     QCOMPARE(qHashRangeCommutative(ints, ints, seed), seed); | 
| 281 |     // verify that order doesn't matter (test not guaranteed): | 
| 282 |     QCOMPARE(qHashRangeCommutative(ints, ints + numInts, seed), | 
| 283 |              qHashRangeCommutative(std::reverse_iterator<int*>(ints + numInts), std::reverse_iterator<int*>(ints), seed)); | 
| 284 |  | 
| 285 |     { | 
| 286 |         // verify that the input iterator category suffices: | 
| 287 |         std::stringstream sstream; | 
| 288 |         std::copy(first: ints, last: ints + numInts, result: std::ostream_iterator<int>(sstream, " " )); | 
| 289 |         sstream.seekg(0); | 
| 290 |         std::istream_iterator<int> it(sstream), end; | 
| 291 |         QCOMPARE(qHashRangeCommutative(ints, ints + numInts, seed), qHashRangeCommutative(it, end, seed)); | 
| 292 |     } | 
| 293 |  | 
| 294 |     { | 
| 295 |         SomeNamespace::Hashable hashables[] = {{.i: 0}, {.i: 1}, {.i: 2}, {.i: 3}, {.i: 4}, {.i: 5}}; | 
| 296 |         // compile check: is qHash() found using ADL? | 
| 297 |         [[maybe_unused]] auto r = qHashRangeCommutative(first: std::begin(arr&: hashables), last: std::end(arr&: hashables), seed); | 
| 298 |     } | 
| 299 |     { | 
| 300 |         SomeNamespace::AdlHashable hashables[] = {{.i: 0}, {.i: 1}, {.i: 2}, {.i: 3}, {.i: 4}, {.i: 5}}; | 
| 301 |         // compile check: is qHash() found as a hidden friend? | 
| 302 |         [[maybe_unused]] auto r = qHashRangeCommutative(first: std::begin(arr&: hashables), last: std::end(arr&: hashables), seed); | 
| 303 |     } | 
| 304 | } | 
| 305 |  | 
| 306 | void tst_QHashFunctions::stdHash() | 
| 307 | { | 
| 308 |     { | 
| 309 |         std::unordered_set<QString> s = {QStringLiteral("Hello" ), QStringLiteral("World" )}; | 
| 310 |         QCOMPARE(s.size(), 2UL); | 
| 311 |         s.insert(QStringLiteral("Hello" )); | 
| 312 |         QCOMPARE(s.size(), 2UL); | 
| 313 |     } | 
| 314 |  | 
| 315 |     { | 
| 316 |         std::unordered_set<QStringView> s = {QStringLiteral("Hello" ), QStringLiteral("World" )}; | 
| 317 |         QCOMPARE(s.size(), 2UL); | 
| 318 |         s.insert(QStringLiteral("Hello" )); | 
| 319 |         QCOMPARE(s.size(), 2UL); | 
| 320 |     } | 
| 321 |  | 
| 322 |     { | 
| 323 |         std::unordered_set<QLatin1String> s = {QLatin1String("Hello" ), QLatin1String("World" )}; | 
| 324 |         QCOMPARE(s.size(), 2UL); | 
| 325 |         s.insert(x: QLatin1String("Hello" )); | 
| 326 |         QCOMPARE(s.size(), 2UL); | 
| 327 |     } | 
| 328 |  | 
| 329 |     { | 
| 330 |         std::unordered_set<QByteArray> s = {QByteArrayLiteral("Hello" ), QByteArrayLiteral("World" )}; | 
| 331 |         QCOMPARE(s.size(), 2UL); | 
| 332 |         s.insert(x: QByteArray("Hello" )); | 
| 333 |         QCOMPARE(s.size(), 2UL); | 
| 334 |     } | 
| 335 |  | 
| 336 | } | 
| 337 |  | 
| 338 | void tst_QHashFunctions::setGlobalQHashSeed() | 
| 339 | { | 
| 340 |     // Setter works as advertised | 
| 341 |     qSetGlobalQHashSeed(newSeed: 0); | 
| 342 |     QCOMPARE(qGlobalQHashSeed(), 0); | 
| 343 |  | 
| 344 |     // Creating a new QHash doesn't reset the seed | 
| 345 |     QHash<QString, int> someHash; | 
| 346 |     someHash.insert(akey: "foo" , avalue: 42); | 
| 347 |     QCOMPARE(qGlobalQHashSeed(), 0); | 
| 348 |  | 
| 349 |     // Reset works as advertised | 
| 350 |     qSetGlobalQHashSeed(newSeed: -1); | 
| 351 |     QVERIFY(qGlobalQHashSeed() > 0); | 
| 352 | } | 
| 353 |  | 
| 354 | QTEST_APPLESS_MAIN(tst_QHashFunctions) | 
| 355 | #include "tst_qhashfunctions.moc" | 
| 356 |  |