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
39class tst_QHashFunctions : public QObject
40{
41 Q_OBJECT
42public:
43 enum {
44 // random value
45 RandomSeed = 1045982819
46 };
47 uint seed;
48
49public slots:
50 void initTestCase();
51 void init();
52
53private 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
69void 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
80void 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
89void tst_QHashFunctions::init()
90{
91 QFETCH_GLOBAL(uint, seedValue);
92 seed = seedValue;
93}
94
95void 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
174void 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
189void 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
196void 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
210void 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
222void tst_QHashFunctions::qthash()
223{
224 QFETCH(QString, key);
225 const uint result = qt_hash(key);
226 QTEST(result, "hash");
227}
228
229namespace 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}
241void 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
274void 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
306void 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
338void 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
354QTEST_APPLESS_MAIN(tst_QHashFunctions)
355#include "tst_qhashfunctions.moc"
356

source code of qtbase/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp