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 <QObject> |
30 | #include <QTest> |
31 | #include <QCache> |
32 | #include <QContiguousCache> |
33 | |
34 | #include <QDebug> |
35 | #include <stdio.h> |
36 | |
37 | class tst_QContiguousCache : public QObject |
38 | { |
39 | Q_OBJECT |
40 | private slots: |
41 | void assignment(); |
42 | |
43 | void empty(); |
44 | void swap(); |
45 | |
46 | void append_data(); |
47 | void append(); |
48 | |
49 | void prepend_data(); |
50 | void prepend(); |
51 | |
52 | void complexType(); |
53 | |
54 | void operatorAt(); |
55 | |
56 | void setCapacity(); |
57 | |
58 | void zeroCapacity(); |
59 | void modifyZeroCapacityCache(); |
60 | }; |
61 | |
62 | QTEST_MAIN(tst_QContiguousCache) |
63 | |
64 | void tst_QContiguousCache::assignment() |
65 | { |
66 | // compile-only test: QTBUG-45783 |
67 | QContiguousCache<int> cc1, cc2; |
68 | // copy: |
69 | cc1 = cc2; |
70 | // move: |
71 | cc1 = std::move(cc2); |
72 | } |
73 | |
74 | void tst_QContiguousCache::empty() |
75 | { |
76 | QContiguousCache<int> c(10); |
77 | QCOMPARE(c.capacity(), 10); |
78 | QCOMPARE(c.count(), 0); |
79 | QVERIFY(c.isEmpty()); |
80 | c.append(value: 1); |
81 | QCOMPARE(c.count(), 1); |
82 | QVERIFY(!c.isEmpty()); |
83 | c.clear(); |
84 | QCOMPARE(c.capacity(), 10); |
85 | QCOMPARE(c.count(), 0); |
86 | QVERIFY(c.isEmpty()); |
87 | c.prepend(value: 1); |
88 | QCOMPARE(c.count(), 1); |
89 | QVERIFY(!c.isEmpty()); |
90 | c.clear(); |
91 | QCOMPARE(c.count(), 0); |
92 | QVERIFY(c.isEmpty()); |
93 | QCOMPARE(c.capacity(), 10); |
94 | } |
95 | |
96 | void tst_QContiguousCache::swap() |
97 | { |
98 | QContiguousCache<int> c1(10), c2(100); |
99 | c1.append(value: 1); |
100 | c1.swap(other&: c2); |
101 | QCOMPARE(c1.capacity(), 100); |
102 | QCOMPARE(c1.count(), 0 ); |
103 | QCOMPARE(c2.capacity(), 10 ); |
104 | QCOMPARE(c2.count(), 1 ); |
105 | } |
106 | |
107 | void tst_QContiguousCache::append_data() |
108 | { |
109 | QTest::addColumn<int>(name: "start" ); |
110 | QTest::addColumn<int>(name: "count" ); |
111 | QTest::addColumn<int>(name: "cacheSize" ); |
112 | QTest::addColumn<bool>(name: "invalidIndexes" ); |
113 | |
114 | QTest::newRow(dataTag: "0+30[10]" ) << 0 << 30 << 10 << false; |
115 | QTest::newRow(dataTag: "300+30[10]" ) << 300 << 30 << 10 << false; |
116 | QTest::newRow(dataTag: "MAX-10+30[10]" ) << INT_MAX-10 << 30 << 10 << true; |
117 | } |
118 | |
119 | void tst_QContiguousCache::append() |
120 | { |
121 | QFETCH(int, start); |
122 | QFETCH(int, count); |
123 | QFETCH(int, cacheSize); |
124 | QFETCH(bool, invalidIndexes); |
125 | |
126 | int i, j; |
127 | QContiguousCache<int> c(cacheSize); |
128 | |
129 | i = 1; |
130 | QCOMPARE(c.available(), cacheSize); |
131 | if (start == 0) |
132 | c.append(value: i++); |
133 | else |
134 | c.insert(pos: start, value: i++); |
135 | while (i < count) { |
136 | c.append(value: i); |
137 | QCOMPARE(c.available(), qMax(0, cacheSize - i)); |
138 | QCOMPARE(c.first(), qMax(1, i-cacheSize+1)); |
139 | QCOMPARE(c.last(), i); |
140 | QCOMPARE(c.count(), qMin(i, cacheSize)); |
141 | QCOMPARE(c.isFull(), i >= cacheSize); |
142 | i++; |
143 | } |
144 | |
145 | QCOMPARE(c.areIndexesValid(), !invalidIndexes); |
146 | if (invalidIndexes) |
147 | c.normalizeIndexes(); |
148 | QVERIFY(c.areIndexesValid()); |
149 | |
150 | // test taking from end until empty. |
151 | for (j = 0; j < cacheSize; j++, i--) { |
152 | QCOMPARE(c.takeLast(), i-1); |
153 | QCOMPARE(c.count(), cacheSize-j-1); |
154 | QCOMPARE(c.available(), j+1); |
155 | QVERIFY(!c.isFull()); |
156 | QCOMPARE(c.isEmpty(), j==cacheSize-1); |
157 | } |
158 | |
159 | } |
160 | |
161 | void tst_QContiguousCache::prepend_data() |
162 | { |
163 | QTest::addColumn<int>(name: "start" ); |
164 | QTest::addColumn<int>(name: "count" ); |
165 | QTest::addColumn<int>(name: "cacheSize" ); |
166 | QTest::addColumn<bool>(name: "invalidIndexes" ); |
167 | |
168 | QTest::newRow(dataTag: "30-30[10]" ) << 30 << 30 << 10 << false; |
169 | QTest::newRow(dataTag: "300-30[10]" ) << 300 << 30 << 10 << false; |
170 | QTest::newRow(dataTag: "10-30[10]" ) << 10 << 30 << 10 << true; |
171 | } |
172 | |
173 | void tst_QContiguousCache::prepend() |
174 | { |
175 | QFETCH(int, start); |
176 | QFETCH(int, count); |
177 | QFETCH(int, cacheSize); |
178 | QFETCH(bool, invalidIndexes); |
179 | |
180 | int i, j; |
181 | QContiguousCache<int> c(cacheSize); |
182 | |
183 | i = 1; |
184 | QCOMPARE(c.available(), cacheSize); |
185 | c.insert(pos: start, value: i++); |
186 | while(i < count) { |
187 | c.prepend(value: i); |
188 | QCOMPARE(c.available(), qMax(0, cacheSize - i)); |
189 | QCOMPARE(c.last(), qMax(1, i-cacheSize+1)); |
190 | QCOMPARE(c.first(), i); |
191 | QCOMPARE(c.count(), qMin(i, cacheSize)); |
192 | QCOMPARE(c.isFull(), i >= cacheSize); |
193 | i++; |
194 | } |
195 | |
196 | QCOMPARE(c.areIndexesValid(), !invalidIndexes); |
197 | if (invalidIndexes) |
198 | c.normalizeIndexes(); |
199 | QVERIFY(c.areIndexesValid()); |
200 | |
201 | // test taking from start until empty. |
202 | for (j = 0; j < cacheSize; j++, i--) { |
203 | QCOMPARE(c.takeFirst(), i-1); |
204 | QCOMPARE(c.count(), cacheSize-j-1); |
205 | QCOMPARE(c.available(), j+1); |
206 | QVERIFY(!c.isFull()); |
207 | QCOMPARE(c.isEmpty(), j==cacheSize-1); |
208 | } |
209 | } |
210 | |
211 | struct RefCountingClassData |
212 | { |
213 | QBasicAtomicInt ref; |
214 | static RefCountingClassData shared_null; |
215 | }; |
216 | |
217 | RefCountingClassData RefCountingClassData::shared_null = { |
218 | Q_BASIC_ATOMIC_INITIALIZER(1) |
219 | }; |
220 | |
221 | class RefCountingClass |
222 | { |
223 | public: |
224 | RefCountingClass() : d(&RefCountingClassData::shared_null) { d->ref.ref(); } |
225 | |
226 | RefCountingClass(const RefCountingClass &other) |
227 | { |
228 | d = other.d; |
229 | d->ref.ref(); |
230 | } |
231 | |
232 | ~RefCountingClass() |
233 | { |
234 | if (!d->ref.deref()) |
235 | delete d; |
236 | } |
237 | |
238 | RefCountingClass &operator=(const RefCountingClass &other) |
239 | { |
240 | if (!d->ref.deref()) |
241 | delete d; |
242 | d = other.d; |
243 | d->ref.ref(); |
244 | return *this; |
245 | } |
246 | |
247 | int refCount() const { return d->ref.loadRelaxed(); } |
248 | private: |
249 | RefCountingClassData *d; |
250 | }; |
251 | |
252 | void tst_QContiguousCache::complexType() |
253 | { |
254 | RefCountingClass original; |
255 | |
256 | QContiguousCache<RefCountingClass> contiguousCache(10); |
257 | contiguousCache.append(value: original); |
258 | QCOMPARE(original.refCount(), 3); |
259 | contiguousCache.removeFirst(); |
260 | QCOMPARE(original.refCount(), 2); // shared null, 'original'. |
261 | contiguousCache.append(value: original); |
262 | QCOMPARE(original.refCount(), 3); |
263 | contiguousCache.clear(); |
264 | QCOMPARE(original.refCount(), 2); |
265 | |
266 | for(int i = 0; i < 100; ++i) |
267 | contiguousCache.insert(pos: i, value: original); |
268 | |
269 | QCOMPARE(original.refCount(), 12); // shared null, 'original', + 10 in contiguousCache. |
270 | |
271 | contiguousCache.clear(); |
272 | QCOMPARE(original.refCount(), 2); |
273 | for (int i = 0; i < 100; i++) |
274 | contiguousCache.append(value: original); |
275 | |
276 | QCOMPARE(original.refCount(), 12); // shared null, 'original', + 10 in contiguousCache. |
277 | contiguousCache.clear(); |
278 | QCOMPARE(original.refCount(), 2); |
279 | |
280 | for (int i = 0; i < 100; i++) |
281 | contiguousCache.prepend(value: original); |
282 | |
283 | QCOMPARE(original.refCount(), 12); // shared null, 'original', + 10 in contiguousCache. |
284 | contiguousCache.clear(); |
285 | QCOMPARE(original.refCount(), 2); |
286 | |
287 | for (int i = 0; i < 100; i++) |
288 | contiguousCache.append(value: original); |
289 | |
290 | contiguousCache.takeLast(); |
291 | QCOMPARE(original.refCount(), 11); |
292 | |
293 | contiguousCache.takeFirst(); |
294 | QCOMPARE(original.refCount(), 10); |
295 | } |
296 | |
297 | void tst_QContiguousCache::operatorAt() |
298 | { |
299 | RefCountingClass original; |
300 | QContiguousCache<RefCountingClass> contiguousCache(10); |
301 | |
302 | for (int i = 25; i < 35; ++i) |
303 | contiguousCache[i] = original; |
304 | |
305 | QCOMPARE(original.refCount(), 12); // shared null, orig, items in cache |
306 | |
307 | // verify const access does not copy items. |
308 | const QContiguousCache<RefCountingClass> copy(contiguousCache); |
309 | for (int i = 25; i < 35; ++i) |
310 | QCOMPARE(copy[i].refCount(), 12); |
311 | |
312 | // verify modifying the original increments ref count (e.g. does a detach) |
313 | contiguousCache[25] = original; |
314 | QCOMPARE(original.refCount(), 22); |
315 | } |
316 | |
317 | void tst_QContiguousCache::setCapacity() |
318 | { |
319 | int i; |
320 | QContiguousCache<int> contiguousCache(100); |
321 | for (i = 280; i < 310; ++i) |
322 | contiguousCache.insert(pos: i, value: i); |
323 | QCOMPARE(contiguousCache.capacity(), 100); |
324 | QCOMPARE(contiguousCache.count(), 30); |
325 | QCOMPARE(contiguousCache.firstIndex(), 280); |
326 | QCOMPARE(contiguousCache.lastIndex(), 309); |
327 | |
328 | for (i = contiguousCache.firstIndex(); i <= contiguousCache.lastIndex(); ++i) { |
329 | QVERIFY(contiguousCache.containsIndex(i)); |
330 | QCOMPARE(contiguousCache.at(i), i); |
331 | } |
332 | |
333 | contiguousCache.setCapacity(150); |
334 | |
335 | QCOMPARE(contiguousCache.capacity(), 150); |
336 | QCOMPARE(contiguousCache.count(), 30); |
337 | QCOMPARE(contiguousCache.firstIndex(), 280); |
338 | QCOMPARE(contiguousCache.lastIndex(), 309); |
339 | |
340 | for (i = contiguousCache.firstIndex(); i <= contiguousCache.lastIndex(); ++i) { |
341 | QVERIFY(contiguousCache.containsIndex(i)); |
342 | QCOMPARE(contiguousCache.at(i), i); |
343 | } |
344 | |
345 | contiguousCache.setCapacity(20); |
346 | |
347 | QCOMPARE(contiguousCache.capacity(), 20); |
348 | QCOMPARE(contiguousCache.count(), 20); |
349 | QCOMPARE(contiguousCache.firstIndex(), 290); |
350 | QCOMPARE(contiguousCache.lastIndex(), 309); |
351 | |
352 | for (i = contiguousCache.firstIndex(); i <= contiguousCache.lastIndex(); ++i) { |
353 | QVERIFY(contiguousCache.containsIndex(i)); |
354 | QCOMPARE(contiguousCache.at(i), i); |
355 | } |
356 | } |
357 | |
358 | void tst_QContiguousCache::zeroCapacity() |
359 | { |
360 | QContiguousCache<int> contiguousCache; |
361 | QCOMPARE(contiguousCache.capacity(),0); |
362 | contiguousCache.setCapacity(10); |
363 | QCOMPARE(contiguousCache.capacity(),10); |
364 | contiguousCache.setCapacity(0); |
365 | QCOMPARE(contiguousCache.capacity(),0); |
366 | } |
367 | |
368 | void tst_QContiguousCache::modifyZeroCapacityCache() |
369 | { |
370 | { |
371 | QContiguousCache<int> contiguousCache; |
372 | contiguousCache.insert(pos: 0, value: 42); |
373 | } |
374 | { |
375 | QContiguousCache<int> contiguousCache; |
376 | contiguousCache.insert(pos: 1, value: 42); |
377 | } |
378 | { |
379 | QContiguousCache<int> contiguousCache; |
380 | contiguousCache.append(value: 42); |
381 | } |
382 | { |
383 | QContiguousCache<int> contiguousCache; |
384 | contiguousCache.prepend(value: 42); |
385 | } |
386 | } |
387 | |
388 | #include "tst_qcontiguouscache.moc" |
389 | |