summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/tools/qcache/tst_qcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/tools/qcache/tst_qcache.cpp')
-rw-r--r--tests/auto/corelib/tools/qcache/tst_qcache.cpp113
1 files changed, 86 insertions, 27 deletions
diff --git a/tests/auto/corelib/tools/qcache/tst_qcache.cpp b/tests/auto/corelib/tools/qcache/tst_qcache.cpp
index f8b0aba2dc..5fccb8f1d0 100644
--- a/tests/auto/corelib/tools/qcache/tst_qcache.cpp
+++ b/tests/auto/corelib/tools/qcache/tst_qcache.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -37,6 +12,7 @@ public slots:
void initTestCase();
void cleanupTestCase();
private slots:
+ void empty();
void maxCost();
void setMaxCost();
void totalCost();
@@ -50,6 +26,7 @@ private slots:
void largeCache();
void internalChainOrderAfterEntryUpdate();
void emplaceLowerCost();
+ void trimWithMovingAcrossSpans();
};
@@ -75,6 +52,21 @@ void tst_QCache::cleanupTestCase()
QCOMPARE(Foo::count, 0);
}
+void tst_QCache::empty()
+{
+ QCache<int, int> cache;
+ QCOMPARE(cache.size(), 0);
+ QCOMPARE(cache.count(), 0);
+ QVERIFY(cache.isEmpty());
+ QVERIFY(!cache.contains(1));
+ QCOMPARE(cache.keys().size(), 0);
+ QCOMPARE(cache.take(1), nullptr);
+ QVERIFY(!cache.remove(1));
+ QCOMPARE(cache.object(1), nullptr);
+ QCOMPARE(cache[1], nullptr);
+ QCOMPARE(cache.totalCost(), 0);
+}
+
void tst_QCache::maxCost()
{
QCache<QString, int> cache1, cache2(100), cache3(200), cache4(-50);
@@ -359,6 +351,7 @@ struct KeyType
int foo;
KeyType(int x) : foo(x) {}
+ constexpr KeyType(const KeyType &o) noexcept : foo(o.foo) {}
private:
KeyType &operator=(const KeyType &);
@@ -445,5 +438,71 @@ void tst_QCache::emplaceLowerCost()
QVERIFY(cache.isEmpty());
}
+struct TrivialHashType {
+ int i = -1;
+ size_t hash = 0;
+
+ TrivialHashType(int i, size_t hash) : i(i), hash(hash) {}
+ TrivialHashType(const TrivialHashType &o) noexcept = default;
+ TrivialHashType &operator=(const TrivialHashType &o) noexcept = default;
+ TrivialHashType(TrivialHashType &&o) noexcept : i(o.i), hash(o.hash) {
+ o.i = -1;
+ o.hash = 0;
+ }
+ TrivialHashType &operator=(TrivialHashType &&o) noexcept {
+ i = o.i;
+ hash = o.hash;
+ o.i = -1;
+ o.hash = 0;
+ return *this;
+ }
+
+
+ friend bool operator==(const TrivialHashType &lhs, const TrivialHashType &rhs)
+ {
+ return lhs.i == rhs.i;
+ }
+};
+quint64 qHash(TrivialHashType t, size_t seed = 0)
+{
+ Q_UNUSED(seed);
+ return t.hash;
+}
+
+// During trim(), if the Node we have a pointer to in the function is moved
+// to another span in the hash table, our pointer would end up pointing to
+// garbage memory. Test that this no longer happens
+void tst_QCache::trimWithMovingAcrossSpans()
+{
+ qsizetype numBuckets = [](){
+ QHash<int, int> h;
+ h.reserve(1);
+ // Beholden to QHash internals:
+ return h.capacity() << 1;
+ }();
+
+ QCache<TrivialHashType, int> cache;
+ cache.setMaxCost(1000);
+
+ auto lastBucketInSpan = size_t(numBuckets - 1);
+ // If this fails then the test is no longer valid
+ QCOMPARE(QHashPrivate::GrowthPolicy::bucketForHash(numBuckets, lastBucketInSpan),
+ lastBucketInSpan);
+
+ // Pad some space so we have two spans:
+ for (int i = 2; i < numBuckets; ++i)
+ cache.insert({i, 0}, nullptr);
+
+ // These two are vying for the last bucket in the first span,
+ // when '0' is deleted, '1' is moved across the span boundary,
+ // invalidating any pointer to its Node.
+ cache.insert({0, lastBucketInSpan}, nullptr);
+ cache.insert({1, lastBucketInSpan}, nullptr);
+
+ QCOMPARE(cache.size(), numBuckets);
+ cache.setMaxCost(0);
+ QCOMPARE(cache.size(), 0);
+}
+
QTEST_APPLESS_MAIN(tst_QCache)
#include "tst_qcache.moc"