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