diff options
Diffstat (limited to 'tests/auto/corelib/tools/qarraydata')
-rw-r--r-- | tests/auto/corelib/tools/qarraydata/CMakeLists.txt | 9 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qarraydata/simplevector.h | 64 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 338 |
3 files changed, 315 insertions, 96 deletions
diff --git a/tests/auto/corelib/tools/qarraydata/CMakeLists.txt b/tests/auto/corelib/tools/qarraydata/CMakeLists.txt index 4a2f9d0d6a..1d84630de2 100644 --- a/tests/auto/corelib/tools/qarraydata/CMakeLists.txt +++ b/tests/auto/corelib/tools/qarraydata/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qarraydata.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qarraydata Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qarraydata LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qarraydata EXCEPTIONS SOURCES diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 747af3d422..b92cd4a887 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QARRAY_TEST_SIMPLE_VECTOR_H @@ -32,6 +7,7 @@ #include <QtCore/qarraydata.h> #include <QtCore/qarraydatapointer.h> +#include <QtCore/qvarlengtharray.h> #include <algorithm> @@ -52,7 +28,7 @@ public: } explicit SimpleVector(size_t n, bool capacityReserved = false) - : d(Data::allocate(n)) + : d(n) { if (n) d->appendInitialize(n); @@ -61,7 +37,7 @@ public: } SimpleVector(size_t n, const T &t, bool capacityReserved = false) - : d(Data::allocate(n)) + : d(n) { if (n) d->copyAppend(n, t); @@ -70,7 +46,7 @@ public: } SimpleVector(const T *begin, const T *end, bool capacityReserved = false) - : d(Data::allocate(end - begin)) + : d(end - begin) { if (end - begin) d->copyAppend(begin, end); @@ -83,11 +59,6 @@ public: { } - explicit SimpleVector(QPair<Data*, T*> ptr, size_t len = 0) - : d(ptr, len) - { - } - SimpleVector(const QArrayDataPointer<T> &other) : d(other) { @@ -159,7 +130,7 @@ public: } } - SimpleVector detached(Data::allocate(qMax(n, size()))); + SimpleVector detached(DataPointer(qMax(n, size()))); if (size()) { detached.d->copyAppend(constBegin(), constEnd()); detached.d->setFlag(QArrayData::CapacityReserved); @@ -173,7 +144,7 @@ public: return; if (d->needsDetach() || newSize > capacity()) { - SimpleVector detached(Data::allocate(d->detachCapacity(newSize))); + SimpleVector detached(DataPointer(d->detachCapacity(newSize))); if (newSize) { if (newSize < size()) { const T *const begin = constBegin(); @@ -209,22 +180,7 @@ public: d->insert(0, first, last - first); } - void append(const_iterator first, const_iterator last) - { - if (first == last) - return; - - auto requiredSize = qsizetype(last - first); - if (d->needsDetach() || d.freeSpaceAtEnd() < requiredSize) { - DataPointer oldData; - d.reallocateAndGrow(QArrayData::GrowsAtEnd, requiredSize, &oldData); - - d->copyAppend(first, last); - return; - } - - d->copyAppend(first, last); - } + void append(const_iterator first, const_iterator last) { d->growAppend(first, last); } void insert(int position, const_iterator first, const_iterator last) { @@ -262,7 +218,7 @@ public: const T *const end = begin + d->size; if (d->needsDetach()) { - SimpleVector detached(Data::allocate(d->detachCapacity(size() - (last - first)))); + SimpleVector detached(DataPointer(d->detachCapacity(size() - (last - first)))); if (first != begin) detached.d->copyAppend(begin, first); detached.d->copyAppend(last, end); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index e29527cb00..e7a84d57ee 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1,31 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include <QTest> #include <QtCore/QString> @@ -37,6 +13,7 @@ #include <tuple> #include <algorithm> #include <vector> +#include <set> #include <stdexcept> #include <functional> #include <memory> @@ -84,6 +61,10 @@ private slots: void dataPointerAllocate(); void selfEmplaceBackwards(); void selfEmplaceForward(); +#ifndef QT_NO_EXCEPTIONS + void relocateWithExceptions_data(); + void relocateWithExceptions(); +#endif // QT_NO_EXCEPTIONS }; template <class T> const T &const_(const T &t) { return t; } @@ -92,7 +73,7 @@ void tst_QArrayData::referenceCounting() { { // Reference counting initialized to 1 (owned) - QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 }; + QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(1), {}, 0 }; QCOMPARE(array.ref_.loadRelaxed(), 1); @@ -126,8 +107,8 @@ void tst_QArrayData::simpleVector() SimpleVector<int> v4(nullptr, data, 0); SimpleVector<int> v5(nullptr, data, 1); SimpleVector<int> v6(nullptr, data, 7); - SimpleVector<int> v7(10, 5); - SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); + const SimpleVector<int> v7(10, 5); + const SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); v3 = v1; v1.swap(v3); @@ -255,7 +236,7 @@ void tst_QArrayData::simpleVector() { int count = 0; - Q_FOREACH (int value, v7) { + for (int value : v7) { QCOMPARE(value, 5); ++count; } @@ -265,7 +246,7 @@ void tst_QArrayData::simpleVector() { int count = 0; - Q_FOREACH (int value, v8) { + for (int value : v8) { QCOMPARE(value, count); ++count; } @@ -503,7 +484,7 @@ void tst_QArrayData::allocate() keeper.headers.append(data); if (grow) - QVERIFY(data->allocatedCapacity() > capacity); + QCOMPARE_GE(data->allocatedCapacity(), capacity); else QCOMPARE(data->allocatedCapacity(), capacity); @@ -1135,8 +1116,7 @@ void tst_QArrayData::arrayOpsExtra() const auto cloneArrayDataPointer = [] (auto &dataPointer, size_t capacity) { using ArrayPointer = std::decay_t<decltype(dataPointer)>; - using Type = std::decay_t<typename ArrayPointer::parameter_type>; - ArrayPointer copy(QTypedArrayData<Type>::allocate(qsizetype(capacity))); + ArrayPointer copy{qsizetype(capacity)}; copy->copyAppend(dataPointer.begin(), dataPointer.end()); return copy; }; @@ -1792,7 +1772,7 @@ void tst_QArrayData::literals() { { QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); - QCOMPARE(d.size, 10u + 1u); + QCOMPARE(d.size, 10 + 1); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], char('A' + i)); } @@ -1815,7 +1795,7 @@ void tst_QArrayData::literals() { // wchar_t is not necessarily 2-bytes QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); - QCOMPARE(d.size, 10u + 1u); + QCOMPARE(d.size, 10 + 1); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], wchar_t('A' + i)); } @@ -1856,7 +1836,7 @@ void tst_QArrayData::variadicLiterals() { QArrayDataPointer<int> d = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - QCOMPARE(d.size, 10u); + QCOMPARE(d.size, 10); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], i); } @@ -1864,7 +1844,7 @@ void tst_QArrayData::variadicLiterals() { QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'); - QCOMPARE(d.size, 10u); + QCOMPARE(d.size, 10); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], char('A' + i)); } @@ -1872,7 +1852,7 @@ void tst_QArrayData::variadicLiterals() { QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"); - QCOMPARE(d.size, 10u); + QCOMPARE(d.size, 10); for (int i = 0; i < 10; ++i) { QCOMPARE(d.data()[i][0], char('A' + i)); QCOMPARE(d.data()[i][1], '\0'); @@ -2056,7 +2036,7 @@ void tst_QArrayData::dataPointerAllocate() const auto createDataPointer = [] (qsizetype capacity, auto initValue) { using Type = std::decay_t<decltype(initValue)>; Q_UNUSED(initValue); - return QArrayDataPointer<Type>(QTypedArrayData<Type>::allocate(capacity)); + return QArrayDataPointer<Type>(capacity); }; const auto testRealloc = [&] (qsizetype capacity, qsizetype newSize, auto initValue) { @@ -2276,5 +2256,281 @@ void tst_QArrayData::selfEmplaceForward() RUN_TEST_FUNC(testSelfEmplace, MyComplexQString(), 4, complexObjs); } +#ifndef QT_NO_EXCEPTIONS +struct ThrowingTypeWatcher +{ + std::vector<void *> destroyedAddrs; + bool watch = false; + + void destroyed(void *addr) + { + if (watch) + destroyedAddrs.push_back(addr); + } +}; + +ThrowingTypeWatcher &throwingTypeWatcher() +{ + static ThrowingTypeWatcher global; + return global; +} + +struct ThrowingType +{ + static unsigned int throwOnce; + static constexpr char throwString[] = "Requested to throw"; + enum MoveCase { + MoveRightNoOverlap, + MoveRightOverlap, + MoveLeftNoOverlap, + MoveLeftOverlap, + }; + enum ThrowCase { + NoThrow, + ThrowInUninitializedRegion, + ThrowInOverlapRegion, + }; + + // reinforce basic checkers with std::shared_ptr which happens to signal + // very explicitly about use-after-free and so on under ASan + std::shared_ptr<int> doubleFreeHelper = std::shared_ptr<int>(new int(42)); + int id = 0; + + void checkThrow() + { + // deferred throw + if (throwOnce > 0) { + --throwOnce; + if (throwOnce == 0) { + throw std::runtime_error(throwString); + } + } + return; + } + + void copy(const ThrowingType &other) noexcept(false) + { + doubleFreeHelper = other.doubleFreeHelper; + id = other.id; + checkThrow(); + } + + ThrowingType(int val = 0) noexcept(false) : id(val) { checkThrow(); } + ThrowingType(const ThrowingType &other) noexcept(false) { copy(other); } + ThrowingType &operator=(const ThrowingType &other) noexcept(false) + { + copy(other); + return *this; + } + ThrowingType(ThrowingType &&other) noexcept(false) { copy(other); } + ThrowingType &operator=(ThrowingType &&other) noexcept(false) + { + copy(other); + return *this; + } + ~ThrowingType() noexcept(true) + { + throwingTypeWatcher().destroyed(this); // notify global watcher + id = -1; + // if we're in dtor but use_count is 0, it's double free + QVERIFY(doubleFreeHelper.use_count() > 0); + } + + friend bool operator==(const ThrowingType &a, const ThrowingType &b) { return a.id == b.id; } +}; + +unsigned int ThrowingType::throwOnce = 0; +static_assert(!QTypeInfo<ThrowingType>::isRelocatable); + +void tst_QArrayData::relocateWithExceptions_data() +{ + QTest::addColumn<ThrowingType::MoveCase>("moveCase"); + QTest::addColumn<ThrowingType::ThrowCase>("throwCase"); + // Not throwing + QTest::newRow("no-throw-move-right-no-overlap") + << ThrowingType::MoveRightNoOverlap << ThrowingType::NoThrow; + QTest::newRow("no-throw-move-right-overlap") + << ThrowingType::MoveRightOverlap << ThrowingType::NoThrow; + QTest::newRow("no-throw-move-left-no-overlap") + << ThrowingType::MoveLeftNoOverlap << ThrowingType::NoThrow; + QTest::newRow("no-throw-move-left-overlap") + << ThrowingType::MoveLeftOverlap << ThrowingType::NoThrow; + // Throwing in uninitialized region + QTest::newRow("throw-in-uninit-region-move-right-no-overlap") + << ThrowingType::MoveRightNoOverlap << ThrowingType::ThrowInUninitializedRegion; + QTest::newRow("throw-in-uninit-region-move-right-overlap") + << ThrowingType::MoveRightOverlap << ThrowingType::ThrowInUninitializedRegion; + QTest::newRow("throw-in-uninit-region-move-left-no-overlap") + << ThrowingType::MoveLeftNoOverlap << ThrowingType::ThrowInUninitializedRegion; + QTest::newRow("throw-in-uninit-region-move-left-overlap") + << ThrowingType::MoveLeftOverlap << ThrowingType::ThrowInUninitializedRegion; + // Throwing in overlap region + QTest::newRow("throw-in-overlap-region-move-right-overlap") + << ThrowingType::MoveRightOverlap << ThrowingType::ThrowInOverlapRegion; + QTest::newRow("throw-in-overlap-region-move-left-overlap") + << ThrowingType::MoveLeftOverlap << ThrowingType::ThrowInOverlapRegion; +} + +void tst_QArrayData::relocateWithExceptions() +{ + // Assume that non-throwing moves perform correctly. Otherwise, all previous + // tests would've failed. Test only what happens when exceptions are thrown. + QFETCH(ThrowingType::MoveCase, moveCase); + QFETCH(ThrowingType::ThrowCase, throwCase); + + struct ThrowingTypeLeakChecker + { + ThrowingType::MoveCase moveCase; + ThrowingType::ThrowCase throwCase; + size_t containerSize = 0; + + ThrowingTypeLeakChecker(ThrowingType::MoveCase mc, ThrowingType::ThrowCase tc) + : moveCase(mc), throwCase(tc) + { + } + + void start(qsizetype size) + { + containerSize = size_t(size); + throwingTypeWatcher().watch = true; + } + + ~ThrowingTypeLeakChecker() + { + const size_t destroyedElementsCount = throwingTypeWatcher().destroyedAddrs.size(); + const size_t destroyedElementsUniqueCount = + std::set<void *>(throwingTypeWatcher().destroyedAddrs.begin(), + throwingTypeWatcher().destroyedAddrs.end()) + .size(); + + // reset the global watcher first and only then verify things + throwingTypeWatcher().watch = false; + throwingTypeWatcher().destroyedAddrs.clear(); + + size_t deletedByRelocate = 0; + switch (throwCase) { + case ThrowingType::NoThrow: + // if no overlap, N elements from old range. otherwise, N - 1 + // elements from old range + if (moveCase == ThrowingType::MoveLeftNoOverlap + || moveCase == ThrowingType::MoveRightNoOverlap) { + deletedByRelocate = containerSize; + } else { + deletedByRelocate = containerSize - 1; + } + break; + case ThrowingType::ThrowInUninitializedRegion: + // 1 relocated element from uninitialized region + deletedByRelocate = 1u; + break; + case ThrowingType::ThrowInOverlapRegion: + // 2 relocated elements from uninitialized region + deletedByRelocate = 2u; + break; + default: + QFAIL("Unknown throwCase"); + } + + QCOMPARE(destroyedElementsCount, deletedByRelocate + containerSize); + QCOMPARE(destroyedElementsUniqueCount, destroyedElementsCount); + } + }; + + const auto setDeferredThrow = [throwCase]() { + switch (throwCase) { + case ThrowingType::NoThrow: + break; // do nothing + case ThrowingType::ThrowInUninitializedRegion: + ThrowingType::throwOnce = 2; + break; + case ThrowingType::ThrowInOverlapRegion: + ThrowingType::throwOnce = 3; + break; + default: + QFAIL("Unknown throwCase"); + } + }; + + const auto createDataPointer = [](qsizetype capacity, qsizetype initSize) { + QArrayDataPointer<ThrowingType> qadp(capacity); + qadp->appendInitialize(initSize); + int i = 0; + std::generate(qadp.begin(), qadp.end(), [&i]() { return ThrowingType(i++); }); + return qadp; + }; + + switch (moveCase) { + case ThrowingType::MoveRightNoOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 3); + QVERIFY(storage.freeSpaceAtEnd() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(4); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + case ThrowingType::MoveRightOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 3); + QVERIFY(storage.freeSpaceAtEnd() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(2); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + case ThrowingType::MoveLeftNoOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 2); + storage->insert(0, 1, ThrowingType(42)); + QVERIFY(storage.freeSpaceAtBegin() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(-4); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + case ThrowingType::MoveLeftOverlap: { + ThrowingTypeLeakChecker watch(moveCase, throwCase); + auto storage = createDataPointer(20, 2); + storage->insert(0, 1, ThrowingType(42)); + QVERIFY(storage.freeSpaceAtBegin() > 3); + + watch.start(storage.size); + try { + setDeferredThrow(); + storage->relocate(-2); + if (throwCase != ThrowingType::NoThrow) + QFAIL("Unreachable line!"); + } catch (const std::runtime_error &e) { + QCOMPARE(std::string(e.what()), ThrowingType::throwString); + } + break; + } + default: + QFAIL("Unknown ThrowingType::MoveCase"); + }; +} +#endif // QT_NO_EXCEPTIONS + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" |