From 03326a2fec416405b437089874f6439e937bbada Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 27 May 2020 09:58:12 +0200 Subject: Move implementation of QVector/List back to qlist.h And name the main class QList. That's also the one we document. This gives less porting pain for our users, and a lot less churn in our API, as we use QList in Qt 5 in 95% of our API. In addition, it gives more consistent naming with QStringList and QByteArrayList and disambiguates QList vs QVector(2|3|4)D. Fixes: QTBUG-84468 Change-Id: I3cba9d1d3179969d8bf9320b31be2230d021d1a9 Reviewed-by: Volker Hilsheimer --- tests/auto/corelib/tools/qlist/.gitignore | 1 + tests/auto/corelib/tools/qlist/CMakeLists.txt | 13 + tests/auto/corelib/tools/qlist/qlist.pro | 7 + tests/auto/corelib/tools/qlist/tst_qlist.cpp | 2834 +++++++++++++++++++++++++ 4 files changed, 2855 insertions(+) create mode 100644 tests/auto/corelib/tools/qlist/.gitignore create mode 100644 tests/auto/corelib/tools/qlist/CMakeLists.txt create mode 100644 tests/auto/corelib/tools/qlist/qlist.pro create mode 100644 tests/auto/corelib/tools/qlist/tst_qlist.cpp (limited to 'tests/auto/corelib/tools/qlist') diff --git a/tests/auto/corelib/tools/qlist/.gitignore b/tests/auto/corelib/tools/qlist/.gitignore new file mode 100644 index 0000000000..5520039486 --- /dev/null +++ b/tests/auto/corelib/tools/qlist/.gitignore @@ -0,0 +1 @@ +tst_qvector diff --git a/tests/auto/corelib/tools/qlist/CMakeLists.txt b/tests/auto/corelib/tools/qlist/CMakeLists.txt new file mode 100644 index 0000000000..fd771de03a --- /dev/null +++ b/tests/auto/corelib/tools/qlist/CMakeLists.txt @@ -0,0 +1,13 @@ +# Generated from qvector.pro. + +##################################################################### +## tst_qvector Test: +##################################################################### + +qt_add_test(tst_qlist + SOURCES + tst_qlist.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/tools/qlist/qlist.pro b/tests/auto/corelib/tools/qlist/qlist.pro new file mode 100644 index 0000000000..17220f377c --- /dev/null +++ b/tests/auto/corelib/tools/qlist/qlist.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +qtConfig(c++11): CONFIG += c++11 +qtConfig(c++14): CONFIG += c++14 +qtConfig(c++1z): CONFIG += c++1z +TARGET = tst_qlist +QT = core testlib +SOURCES = $$PWD/tst_qlist.cpp diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp new file mode 100644 index 0000000000..df3f2023d6 --- /dev/null +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -0,0 +1,2834 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +struct Movable { + Movable(char input = 'j') + : i(input) + , that(this) + , state(Constructed) + { + counter.fetchAndAddRelaxed(1); + } + Movable(const Movable &other) + : i(other.i) + , that(this) + , state(Constructed) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + } + Movable(Movable &&other) + : i(other.i) + , that(other.that) + , state(Constructed) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + other.that = nullptr; + } + + ~Movable() + { + check(state, Constructed); + i = 0; + counter.fetchAndAddRelaxed(-1); + state = Destructed; + } + + bool operator ==(const Movable &other) const + { + check(state, Constructed); + check(other.state, Constructed); + return i == other.i; + } + + Movable &operator=(const Movable &other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = this; + return *this; + } + Movable &operator=(Movable &&other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = other.that; + other.that = nullptr; + return *this; + } + bool wasConstructedAt(const Movable *other) const + { + return that == other; + } + char i; + static QAtomicInt counter; +private: + Movable *that; // used to check if an instance was moved + + enum State { Constructed = 106, Destructed = 110 }; + State state; + + static void check(const State state1, const State state2) + { + QCOMPARE(int(state1), int(state2)); + } +}; + +inline size_t qHash(const Movable &key, size_t seed = 0) { return qHash(key.i, seed); } + +QAtomicInt Movable::counter = 0; +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +QT_END_NAMESPACE +Q_DECLARE_METATYPE(Movable); + +struct Custom { + Custom(char input = 'j') + : i(input) + , that(this) + , state(Constructed) + { + counter.fetchAndAddRelaxed(1); + } + Custom(const Custom &other) + : that(this) + , state(Constructed) + { + check(&other); + counter.fetchAndAddRelaxed(1); + this->i = other.i; + } + ~Custom() + { + check(this); + i = 0; + counter.fetchAndAddRelaxed(-1); + state = Destructed; + } + + bool operator ==(const Custom &other) const + { + check(&other); + check(this); + return i == other.i; + } + + bool operator<(const Custom &other) const + { + check(&other); + check(this); + return i < other.i; + } + + Custom &operator=(const Custom &other) + { + check(&other); + check(this); + i = other.i; + return *this; + } + static QAtomicInt counter; + + char i; // used to identify orgin of an instance +private: + Custom *that; // used to check if an instance was moved + + enum State { Constructed = 106, Destructed = 110 }; + State state; + + static void check(const Custom *c) + { + // check if c object has been moved + QCOMPARE(c, c->that); + QCOMPARE(int(c->state), int(Constructed)); + } +}; +QAtomicInt Custom::counter = 0; + +inline size_t qHash(const Custom &key, size_t seed = 0) { return qHash(key.i, seed); } + +Q_DECLARE_METATYPE(Custom); + +// tests depends on the fact that: +static_assert(!QTypeInfo::isStatic); +static_assert(!QTypeInfo::isComplex); +static_assert(!QTypeInfo::isStatic); +static_assert(QTypeInfo::isComplex); +static_assert(QTypeInfo::isStatic); +static_assert(QTypeInfo::isComplex); + + +class tst_QList : public QObject +{ + Q_OBJECT +private slots: + void constructors_empty() const; + void constructors_emptyReserveZero() const; + void constructors_emptyReserve() const; + void constructors_reserveAndInitialize() const; + void copyConstructorInt() const; + void copyConstructorMovable() const; + void copyConstructorCustom() const; + void assignmentInt() const; + void assignmentMovable() const; + void assignmentCustom() const; + void assignFromInitializerListInt() const; + void assignFromInitializerListMovable() const; + void assignFromInitializerListCustom() const; + void addInt() const; + void addMovable() const; + void addCustom() const; + void appendInt() const; + void appendMovable() const; + void appendCustom() const; + void appendRvalue() const; + void at() const; + void capacityInt() const; + void capacityMovable() const; + void capacityCustom() const; + void clearInt() const; + void clearMovable() const; + void clearCustom() const; + void constData() const; + void constFirst() const; + void constLast() const; + void contains() const; + void countInt() const; + void countMovable() const; + void countCustom() const; + void cpp17ctad() const; + void data() const; + void emptyInt() const; + void emptyMovable() const; + void emptyCustom() const; + void endsWith() const; + void eraseEmptyInt() const; + void eraseEmptyMovable() const; + void eraseEmptyCustom() const; + void eraseEmptyReservedInt() const; + void eraseEmptyReservedMovable() const; + void eraseEmptyReservedCustom() const; + void eraseInt() const; + void eraseIntShared() const; + void eraseMovable() const; + void eraseMovableShared() const; + void eraseCustom() const; + void eraseCustomShared() const; + void eraseReservedInt() const; + void eraseReservedMovable() const; + void eraseReservedCustom() const; + void fillInt() const; + void fillMovable() const; + void fillCustom() const; + void fillDetaches() const; + void first() const; + void fromListInt() const; + void fromListMovable() const; + void fromListCustom() const; + void indexOf() const; + void insertInt() const; + void insertMovable() const; + void insertCustom() const; + void isEmpty() const; + void last() const; + void lastIndexOf() const; + void mid() const; + void moveInt() const; + void moveMovable() const; + void moveCustom() const; + void prependInt() const; + void prependMovable() const; + void prependCustom() const; + void qhashInt() const { qhash(); } + void qhashMovable() const { qhash(); } + void qhashCustom() const { qhash(); } + void removeAllWithAlias() const; + void removeInt() const; + void removeMovable() const; + void removeCustom() const; + void removeFirstLast() const; + void resizePOD_data() const; + void resizePOD() const; + void resizeComplexMovable_data() const; + void resizeComplexMovable() const; + void resizeComplex_data() const; + void resizeComplex() const; + void resizeCtorAndDtor() const; + void reverseIterators() const; + void sizeInt() const; + void sizeMovable() const; + void sizeCustom() const; + void startsWith() const; + void swapInt() const; + void swapMovable() const; + void swapCustom() const; + void toList() const; +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + void fromStdVector() const; + void toStdVector() const; +#endif + void value() const; + + void testOperators() const; + + void reserve(); + void reserveZero(); + void initializeListInt(); + void initializeListMovable(); + void initializeListCustom(); + + void const_shared_null(); + + void detachInt() const; + void detachMovable() const; + void detachCustom() const; + void detachThreadSafetyInt() const; + void detachThreadSafetyMovable() const; + void detachThreadSafetyCustom() const; + + void insertMove() const; + + void swapItemsAt() const; + + void emplaceInt(); + void emplaceCustom(); + void emplaceMovable(); + void emplaceConsistentWithStdVectorInt(); + void emplaceConsistentWithStdVectorCustom(); + void emplaceConsistentWithStdVectorMovable(); + void emplaceReturnsIterator(); + void emplaceBack(); + void emplaceBackReturnsRef(); + void emplaceWithElementFromTheSameContainer(); + void emplaceWithElementFromTheSameContainer_data(); + +private: + template void copyConstructor() const; + template void add() const; + template void append() const; + template void assignFromInitializerList() const; + template void capacity() const; + template void clear() const; + template void count() const; + template void empty() const; + template void eraseEmpty() const; + template void eraseEmptyReserved() const; + template void erase(bool shared) const; + template void eraseReserved() const; + template void fill() const; + template void fromList() const; + template void insert() const; + template void qhash() const; + template void move() const; + template void prepend() const; + template void remove() const; + template void size() const; + template void swap() const; + template void initializeList(); + template void detach() const; + template void detachThreadSafety() const; + template void emplaceImpl() const; + template void emplaceConsistentWithStdVectorImpl() const; +}; + + +template struct SimpleValue +{ + static T at(int index) + { + return Values[index % MaxIndex]; + } + + static QList vector(int size) + { + QList ret; + for (int i = 0; i < size; i++) + ret.append(at(i)); + return ret; + } + + static const uint MaxIndex = 6; + static const T Values[MaxIndex]; +}; + +template<> +const int SimpleValue::Values[] = { 110, 105, 101, 114, 111, 98 }; +template<> +const Movable SimpleValue::Values[] = { 110, 105, 101, 114, 111, 98 }; +template<> +const Custom SimpleValue::Values[] = { 110, 105, 101, 114, 111, 98 }; + +// Make some macros for the tests to use in order to be slightly more readable... +#define T_FOO SimpleValue::at(0) +#define T_BAR SimpleValue::at(1) +#define T_BAZ SimpleValue::at(2) +#define T_CAT SimpleValue::at(3) +#define T_DOG SimpleValue::at(4) +#define T_BLAH SimpleValue::at(5) + +void tst_QList::constructors_empty() const +{ + QList emptyInt; + QList emptyMovable; + QList emptyCustom; +} + +void tst_QList::constructors_emptyReserveZero() const +{ + QList emptyInt(0); + QList emptyMovable(0); + QList emptyCustom(0); +} + +void tst_QList::constructors_emptyReserve() const +{ + // pre-reserve capacity + QList myInt(5); + QVERIFY(myInt.capacity() == 5); + QList myMovable(5); + QVERIFY(myMovable.capacity() == 5); + QList myCustom(4); + QVERIFY(myCustom.capacity() == 4); +} + +void tst_QList::constructors_reserveAndInitialize() const +{ + // default-initialise items + + QList myInt(5, 42); + QVERIFY(myInt.capacity() == 5); + foreach (int meaningoflife, myInt) { + QCOMPARE(meaningoflife, 42); + } + + QList myString(5, QString::fromLatin1("c++")); + QVERIFY(myString.capacity() == 5); + // make sure all items are initialised ok + foreach (QString meaningoflife, myString) { + QCOMPARE(meaningoflife, QString::fromLatin1("c++")); + } + + QList myCustom(5, Custom('n')); + QVERIFY(myCustom.capacity() == 5); + // make sure all items are initialised ok + foreach (Custom meaningoflife, myCustom) { + QCOMPARE(meaningoflife.i, 'n'); + } +} + +template +void tst_QList::copyConstructor() const +{ + T value1(SimpleValue::at(0)); + T value2(SimpleValue::at(1)); + T value3(SimpleValue::at(2)); + T value4(SimpleValue::at(3)); + { + QList v1; + QList v2(v1); + QCOMPARE(v1, v2); + } + { + QList v1; + v1 << value1 << value2 << value3 << value4; + QList v2(v1); + QCOMPARE(v1, v2); + } +} + +void tst_QList::copyConstructorInt() const +{ + copyConstructor(); +} + +void tst_QList::copyConstructorMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + copyConstructor(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::copyConstructorCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + copyConstructor(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +static inline void testAssignment() +{ + QList v1(5); + QCOMPARE(v1.size(), 5); + QVERIFY(v1.isDetached()); + + QList v2(7); + QCOMPARE(v2.size(), 7); + QVERIFY(v2.isDetached()); + + QVERIFY(!v1.isSharedWith(v2)); + + v1 = v2; + + QVERIFY(!v1.isDetached()); + QVERIFY(!v2.isDetached()); + QVERIFY(v1.isSharedWith(v2)); + + const void *const data1 = v1.constData(); + const void *const data2 = v2.constData(); + + QCOMPARE(data1, data2); + + v1.clear(); + + QVERIFY(v2.isDetached()); + QVERIFY(!v1.isSharedWith(v2)); + QCOMPARE((void *)v2.constData(), data2); +} + +void tst_QList::assignmentInt() const +{ + testAssignment(); +} + +void tst_QList::assignmentMovable() const +{ + testAssignment(); +} + +void tst_QList::assignmentCustom() const +{ + testAssignment(); +} + +template +void tst_QList::assignFromInitializerList() const +{ + T val1(SimpleValue::at(1)); + T val2(SimpleValue::at(2)); + T val3(SimpleValue::at(3)); + + QList v1 = {val1, val2, val3}; + QCOMPARE(v1, QList() << val1 << val2 << val3); + QCOMPARE(v1, (QList {val1, val2, val3})); + + v1 = {}; + QCOMPARE(v1.size(), 0); +} + +void tst_QList::assignFromInitializerListInt() const +{ + assignFromInitializerList(); +} + +void tst_QList::assignFromInitializerListMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + assignFromInitializerList(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::assignFromInitializerListCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + assignFromInitializerList(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +void tst_QList::add() const +{ + { + QList empty1; + QList empty2; + QVERIFY((empty1 + empty2).isEmpty()); + empty1 += empty2; + QVERIFY(empty1.isEmpty()); + QVERIFY(empty2.isEmpty()); + } + { + QList v(12); + QList empty; + QCOMPARE((v + empty), v); + v += empty; + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), 12); + QVERIFY(empty.isEmpty()); + } + { + QList v1(12); + QList v2; + v2 += v1; + QVERIFY(!v1.isEmpty()); + QCOMPARE(v1.size(), 12); + QVERIFY(!v2.isEmpty()); + QCOMPARE(v2.size(), 12); + } +} + +void tst_QList::addInt() const +{ + add(); +} + +void tst_QList::addMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + add(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::addCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + add(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +void tst_QList::append() const +{ + { + QList myvec; + myvec.append(SimpleValue::at(0)); + QVERIFY(myvec.size() == 1); + myvec.append(SimpleValue::at(1)); + QVERIFY(myvec.size() == 2); + myvec.append(SimpleValue::at(2)); + QVERIFY(myvec.size() == 3); + + QCOMPARE(myvec, QList() << SimpleValue::at(0) + << SimpleValue::at(1) + << SimpleValue::at(2)); + } + { + QList v(2); + v.append(SimpleValue::at(0)); + QVERIFY(v.size() == 3); + QCOMPARE(v.at(v.size() - 1), SimpleValue::at(0)); + } + { + QList v(2); + v.reserve(12); + v.append(SimpleValue::at(0)); + QVERIFY(v.size() == 3); + QCOMPARE(v.at(v.size() - 1), SimpleValue::at(0)); + } + { + QList v; + v << 1 << 2 << 3; + QList x; + x << 4 << 5 << 6; + v.append(x); + + QList combined; + combined << 1 << 2 << 3 << 4 << 5 << 6; + + QCOMPARE(v, combined); + } +} + +void tst_QList::appendInt() const +{ + append(); +} + +void tst_QList::appendMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + append(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::appendCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + append(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::appendRvalue() const +{ + QList v; + v.append("hello"); + QString world = "world"; + v.append(std::move(world)); + QVERIFY(world.isEmpty()); + QCOMPARE(v.front(), QString("hello")); + QCOMPARE(v.back(), QString("world")); +} + +void tst_QList::at() const +{ + QList myvec; + myvec << "foo" << "bar" << "baz"; + + QVERIFY(myvec.size() == 3); + QCOMPARE(myvec.at(0), QLatin1String("foo")); + QCOMPARE(myvec.at(1), QLatin1String("bar")); + QCOMPARE(myvec.at(2), QLatin1String("baz")); + + // append an item + myvec << "hello"; + QVERIFY(myvec.size() == 4); + QCOMPARE(myvec.at(0), QLatin1String("foo")); + QCOMPARE(myvec.at(1), QLatin1String("bar")); + QCOMPARE(myvec.at(2), QLatin1String("baz")); + QCOMPARE(myvec.at(3), QLatin1String("hello")); + + // remove an item + myvec.remove(1); + QVERIFY(myvec.size() == 3); + QCOMPARE(myvec.at(0), QLatin1String("foo")); + QCOMPARE(myvec.at(1), QLatin1String("baz")); + QCOMPARE(myvec.at(2), QLatin1String("hello")); +} + +template +void tst_QList::capacity() const +{ + QList myvec; + + // TODO: is this guaranteed? seems a safe assumption, but I suppose preallocation of a + // few items isn't an entirely unforseeable possibility. + QVERIFY(myvec.capacity() == 0); + + // test it gets a size + myvec << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2); + QVERIFY(myvec.capacity() >= 3); + + // make sure it grows ok + myvec << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2); + QVERIFY(myvec.capacity() >= 6); + // let's try squeeze a bit + myvec.remove(3); + myvec.remove(3); + myvec.remove(3); + myvec.squeeze(); + QVERIFY(myvec.capacity() >= 3); + + myvec.remove(0); + myvec.remove(0); + myvec.remove(0); + myvec.squeeze(); + QVERIFY(myvec.capacity() == 0); +} + +void tst_QList::capacityInt() const +{ + capacity(); +} + +void tst_QList::capacityMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + capacity(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::capacityCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + capacity(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +void tst_QList::clear() const +{ + QList myvec; + myvec << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2); + + const auto oldCapacity = myvec.capacity(); + QCOMPARE(myvec.size(), 3); + myvec.clear(); + QCOMPARE(myvec.size(), 0); + QCOMPARE(myvec.capacity(), oldCapacity); +} + +void tst_QList::clearInt() const +{ + clear(); +} + +void tst_QList::clearMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + clear(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::clearCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + clear(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::constData() const +{ + int arr[] = { 42, 43, 44 }; + QList myvec; + myvec << 42 << 43 << 44; + + QVERIFY(memcmp(myvec.constData(), reinterpret_cast(&arr), sizeof(int) * 3) == 0); +} + +void tst_QList::contains() const +{ + QList myvec; + myvec << "aaa" << "bbb" << "ccc"; + + QVERIFY(myvec.contains(QLatin1String("aaa"))); + QVERIFY(myvec.contains(QLatin1String("bbb"))); + QVERIFY(myvec.contains(QLatin1String("ccc"))); + QVERIFY(!myvec.contains(QLatin1String("I don't exist"))); + + // add it and make sure it does :) + myvec.append(QLatin1String("I don't exist")); + QVERIFY(myvec.contains(QLatin1String("I don't exist"))); +} + +template +void tst_QList::count() const +{ + // total size + { + // zero size + QList myvec; + QVERIFY(myvec.count() == 0); + + // grow + myvec.append(SimpleValue::at(0)); + QVERIFY(myvec.count() == 1); + myvec.append(SimpleValue::at(1)); + QVERIFY(myvec.count() == 2); + + // shrink + myvec.remove(0); + QVERIFY(myvec.count() == 1); + myvec.remove(0); + QVERIFY(myvec.count() == 0); + } + + // count of items + { + QList myvec; + myvec << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2); + + // initial tests + QVERIFY(myvec.count(SimpleValue::at(0)) == 1); + QVERIFY(myvec.count(SimpleValue::at(3)) == 0); + + // grow + myvec.append(SimpleValue::at(0)); + QVERIFY(myvec.count(SimpleValue::at(0)) == 2); + + // shrink + myvec.remove(0); + QVERIFY(myvec.count(SimpleValue::at(0)) == 1); + } +} + +void tst_QList::countInt() const +{ + count(); +} + +void tst_QList::countMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + count(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::countCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + count(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::cpp17ctad() const +{ +#ifdef __cpp_deduction_guides +#define QVERIFY_IS_VECTOR_OF(obj, Type) \ + QVERIFY2((std::is_same>::value), \ + QMetaType::typeName(qMetaTypeId())) +#define CHECK(Type, One, Two, Three) \ + do { \ + const Type v[] = {One, Two, Three}; \ + QList v1 = {One, Two, Three}; \ + QVERIFY_IS_VECTOR_OF(v1, Type); \ + QList v2(v1.begin(), v1.end()); \ + QVERIFY_IS_VECTOR_OF(v2, Type); \ + QList v3(std::begin(v), std::end(v)); \ + QVERIFY_IS_VECTOR_OF(v3, Type); \ + } while (false) \ + /*end*/ + CHECK(int, 1, 2, 3); + CHECK(double, 1.0, 2.0, 3.0); + CHECK(QString, QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three")); +#undef QVERIFY_IS_VECTOR_OF +#undef CHECK +#else + QSKIP("This test requires C++17 Constructor Template Argument Deduction support enabled in the compiler."); +#endif +} + +void tst_QList::data() const +{ + QList myvec; + myvec << 42 << 43 << 44; + + // make sure it starts off ok + QCOMPARE(*(myvec.data() + 1), 43); + + // alter it + *(myvec.data() + 1) = 69; + + // check it altered + QCOMPARE(*(myvec.data() + 1), 69); + + int arr[] = { 42, 69, 44 }; + QVERIFY(memcmp(myvec.data(), reinterpret_cast(&arr), sizeof(int) * 3) == 0); +} + +template +void tst_QList::empty() const +{ + QList myvec; + + // starts empty + QVERIFY(myvec.empty()); + + // not empty + myvec.append(SimpleValue::at(2)); + QVERIFY(!myvec.empty()); + + // empty again + myvec.remove(0); + QVERIFY(myvec.empty()); +} + +void tst_QList::emptyInt() const +{ + empty(); +} + +void tst_QList::emptyMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + empty(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::emptyCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + empty(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::endsWith() const +{ + QList myvec; + + // empty vector + QVERIFY(!myvec.endsWith(1)); + + // add the one, should work + myvec.append(1); + QVERIFY(myvec.endsWith(1)); + + // add something else, fails now + myvec.append(3); + QVERIFY(!myvec.endsWith(1)); + + // remove it again :) + myvec.remove(1); + QVERIFY(myvec.endsWith(1)); +} + +template +void tst_QList::eraseEmpty() const +{ + QList v; + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); +} + +void tst_QList::eraseEmptyInt() const +{ + eraseEmpty(); +} + +void tst_QList::eraseEmptyMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + eraseEmpty(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::eraseEmptyCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + eraseEmpty(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +void tst_QList::eraseEmptyReserved() const +{ + QList v; + v.reserve(10); + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); +} + +void tst_QList::eraseEmptyReservedInt() const +{ + eraseEmptyReserved(); +} + +void tst_QList::eraseEmptyReservedMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + eraseEmptyReserved(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::eraseEmptyReservedCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + eraseEmptyReserved(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +struct SharedVectorChecker +{ + SharedVectorChecker(const QList &original, bool doCopyVector) + : originalSize(-1), + copy(0) + { + if (doCopyVector) { + originalSize = original.size(); + copy = new QList(original); + // this is unlikely to fail, but if the check in the destructor fails it's good to know that + // we were still alright here. + QCOMPARE(originalSize, copy->size()); + } + } + + ~SharedVectorChecker() + { + if (copy) + QCOMPARE(copy->size(), originalSize); + delete copy; + } + + int originalSize; + QList *copy; +}; + +template +void tst_QList::erase(bool shared) const +{ + // note: remove() is actually more efficient, and more dangerous, because it uses the non-detaching + // begin() / end() internally. you can also use constBegin() and constEnd() with erase(), but only + // using reinterpret_cast... because both iterator types are really just pointers. + // so we use a mix of erase() and remove() to cover more cases. + { + QList v = SimpleValue::vector(12); + SharedVectorChecker svc(v, shared); + v.erase(v.begin()); + QCOMPARE(v.size(), 11); + for (int i = 0; i < 11; i++) + QCOMPARE(v.at(i), SimpleValue::at(i + 1)); + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); + if (shared) + QCOMPARE(SimpleValue::vector(12), *svc.copy); + } + { + QList v = SimpleValue::vector(12); + SharedVectorChecker svc(v, shared); + v.remove(1); + QCOMPARE(v.size(), 11); + QCOMPARE(v.at(0), SimpleValue::at(0)); + for (int i = 1; i < 11; i++) + QCOMPARE(v.at(i), SimpleValue::at(i + 1)); + v.erase(v.begin() + 1, v.end()); + QCOMPARE(v.size(), 1); + QCOMPARE(v.at(0), SimpleValue::at(0)); + if (shared) + QCOMPARE(SimpleValue::vector(12), *svc.copy); + } + { + QList v = SimpleValue::vector(12); + SharedVectorChecker svc(v, shared); + v.erase(v.begin(), v.end() - 1); + QCOMPARE(v.size(), 1); + QCOMPARE(v.at(0), SimpleValue::at(11)); + if (shared) + QCOMPARE(SimpleValue::vector(12), *svc.copy); + } + { + QList v = SimpleValue::vector(12); + SharedVectorChecker svc(v, shared); + v.remove(5); + QCOMPARE(v.size(), 11); + for (int i = 0; i < 5; i++) + QCOMPARE(v.at(i), SimpleValue::at(i)); + for (int i = 5; i < 11; i++) + QCOMPARE(v.at(i), SimpleValue::at(i + 1)); + v.erase(v.begin() + 1, v.end() - 1); + QCOMPARE(v.at(0), SimpleValue::at(0)); + QCOMPARE(v.at(1), SimpleValue::at(11)); + QCOMPARE(v.size(), 2); + if (shared) + QCOMPARE(SimpleValue::vector(12), *svc.copy); + } +} + +void tst_QList::eraseInt() const +{ + erase(false); +} + +void tst_QList::eraseIntShared() const +{ + erase(true); +} + +void tst_QList::eraseMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + erase(false); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::eraseMovableShared() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + erase(true); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::eraseCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + erase(false); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::eraseCustomShared() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + erase(true); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template void tst_QList::eraseReserved() const +{ + { + QList v(12); + v.reserve(16); + v.erase(v.begin()); + QCOMPARE(v.size(), 11); + v.erase(v.begin(), v.end()); + QCOMPARE(v.size(), 0); + } + { + QList v(12); + v.reserve(16); + v.erase(v.begin() + 1); + QCOMPARE(v.size(), 11); + v.erase(v.begin() + 1, v.end()); + QCOMPARE(v.size(), 1); + } + { + QList v(12); + v.reserve(16); + v.erase(v.begin(), v.end() - 1); + QCOMPARE(v.size(), 1); + } + { + QList v(12); + v.reserve(16); + v.erase(v.begin() + 5); + QCOMPARE(v.size(), 11); + v.erase(v.begin() + 1, v.end() - 1); + QCOMPARE(v.size(), 2); + } +} + +void tst_QList::eraseReservedInt() const +{ + eraseReserved(); +} + +void tst_QList::eraseReservedMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + eraseReserved(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::eraseReservedCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + eraseReserved(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +void tst_QList::fill() const +{ + QList myvec; + + // resize + myvec.resize(5); + myvec.fill(SimpleValue::at(1)); + QCOMPARE(myvec, QList() << SimpleValue::at(1) << SimpleValue::at(1) + << SimpleValue::at(1) << SimpleValue::at(1) + << SimpleValue::at(1)); + + // make sure it can resize itself too + myvec.fill(SimpleValue::at(2), 10); + QCOMPARE(myvec, QList() << SimpleValue::at(2) << SimpleValue::at(2) + << SimpleValue::at(2) << SimpleValue::at(2) + << SimpleValue::at(2) << SimpleValue::at(2) + << SimpleValue::at(2) << SimpleValue::at(2) + << SimpleValue::at(2) << SimpleValue::at(2)); +} + +void tst_QList::fillInt() const +{ + fill(); +} + +void tst_QList::fillMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + fill(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::fillCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + fill(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::fillDetaches() const +{ + QList test = { 1, 2, 3 }; + QList copy = test; + copy.fill(42); + + QCOMPARE(test, QList({1, 2, 3})); + QCOMPARE(copy, QList({42, 42, 42})); +} + +void tst_QList::first() const +{ + QList myvec; + myvec << 69 << 42 << 3; + + // test it starts ok + QCOMPARE(myvec.first(), 69); + QCOMPARE(myvec.constFirst(), 69); + + // test removal changes + myvec.remove(0); + QCOMPARE(myvec.first(), 42); + QCOMPARE(myvec.constFirst(), 42); + + // test prepend changes + myvec.prepend(23); + QCOMPARE(myvec.first(), 23); + QCOMPARE(myvec.constFirst(), 23); +} + +void tst_QList::constFirst() const +{ + QList myvec; + myvec << 69 << 42 << 3; + + // test it starts ok + QCOMPARE(myvec.constFirst(), 69); + QVERIFY(myvec.isDetached()); + + QList myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constFirst(), 69); + QCOMPARE(myvecCopy.constFirst(), 69); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test removal changes + myvec.remove(0); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constFirst(), 42); + QCOMPARE(myvecCopy.constFirst(), 69); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constFirst(), 42); + QCOMPARE(myvecCopy.constFirst(), 42); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test prepend changes + myvec.prepend(23); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constFirst(), 23); + QCOMPARE(myvecCopy.constFirst(), 42); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constFirst(), 23); + QCOMPARE(myvecCopy.constFirst(), 23); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); +} + + +template +void tst_QList::fromList() const +{ + QList list; + list << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2) << SimpleValue::at(3); + + QList myvec; + myvec = QList::fromList(list); + + // test it worked ok + QCOMPARE(myvec, QList() << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2) << SimpleValue::at(3)); + QCOMPARE(list, QList() << SimpleValue::at(0) << SimpleValue::at(1) << SimpleValue::at(2) << SimpleValue::at(3)); +} + +void tst_QList::fromListInt() const +{ + fromList(); +} + +void tst_QList::fromListMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + fromList(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::fromListCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + fromList(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +void tst_QList::fromStdVector() const +{ + // stl = :( + std::vector svec; + svec.push_back(QLatin1String("aaa")); + svec.push_back(QLatin1String("bbb")); + svec.push_back(QLatin1String("ninjas")); + svec.push_back(QLatin1String("pirates")); + QList myvec = QList::fromStdVector(svec); + + // test it converts ok + QCOMPARE(myvec, QList() << "aaa" << "bbb" << "ninjas" << "pirates"); +} +#endif + +void tst_QList::indexOf() const +{ + QList myvec; + myvec << "A" << "B" << "C" << "B" << "A"; + + QVERIFY(myvec.indexOf("B") == 1); + QVERIFY(myvec.indexOf("B", 1) == 1); + QVERIFY(myvec.indexOf("B", 2) == 3); + QVERIFY(myvec.indexOf("X") == -1); + QVERIFY(myvec.indexOf("X", 2) == -1); + + // add an X + myvec << "X"; + QVERIFY(myvec.indexOf("X") == 5); + QVERIFY(myvec.indexOf("X", 5) == 5); + QVERIFY(myvec.indexOf("X", 6) == -1); + + // remove first A + myvec.remove(0); + QVERIFY(myvec.indexOf("A") == 3); + QVERIFY(myvec.indexOf("A", 3) == 3); + QVERIFY(myvec.indexOf("A", 4) == -1); +} + +template +void tst_QList::insert() const +{ + QList myvec; + const T + tA = SimpleValue::at(0), + tB = SimpleValue::at(1), + tC = SimpleValue::at(2), + tX = SimpleValue::at(3), + tZ = SimpleValue::at(4), + tT = SimpleValue::at(5), + ti = SimpleValue::at(6); + myvec << tA << tB << tC; + QList myvec2 = myvec; + + // first position + QCOMPARE(myvec.at(0), tA); + myvec.insert(0, tX); + QCOMPARE(myvec.at(0), tX); + QCOMPARE(myvec.at(1), tA); + + QCOMPARE(myvec2.at(0), tA); + myvec2.insert(myvec2.begin(), tX); + QCOMPARE(myvec2.at(0), tX); + QCOMPARE(myvec2.at(1), tA); + + // middle + myvec.insert(1, tZ); + QCOMPARE(myvec.at(0), tX); + QCOMPARE(myvec.at(1), tZ); + QCOMPARE(myvec.at(2), tA); + + myvec2.insert(myvec2.begin() + 1, tZ); + QCOMPARE(myvec2.at(0), tX); + QCOMPARE(myvec2.at(1), tZ); + QCOMPARE(myvec2.at(2), tA); + + // end + myvec.insert(5, tT); + QCOMPARE(myvec.at(5), tT); + QCOMPARE(myvec.at(4), tC); + + myvec2.insert(myvec2.end(), tT); + QCOMPARE(myvec2.at(5), tT); + QCOMPARE(myvec2.at(4), tC); + + // insert a lot of garbage in the middle + myvec.insert(2, 2, ti); + QCOMPARE(myvec, QList() << tX << tZ << ti << ti + << tA << tB << tC << tT); + + myvec2.insert(myvec2.begin() + 2, 2, ti); + QCOMPARE(myvec2, myvec); + + // insert from references to the same container: + myvec.insert(0, 1, myvec[5]); // inserts tB + myvec2.insert(0, 1, myvec2[5]); // inserts tB + QCOMPARE(myvec, QList() << tB << tX << tZ << ti << ti + << tA << tB << tC << tT); + QCOMPARE(myvec2, myvec); + + myvec.insert(0, 1, const_cast&>(myvec)[0]); // inserts tB + myvec2.insert(0, 1, const_cast&>(myvec2)[0]); // inserts tB + QCOMPARE(myvec, QList() << tB << tB << tX << tZ << ti << ti + << tA << tB << tC << tT); + QCOMPARE(myvec2, myvec); +} + +void tst_QList::insertInt() const +{ + insert(); +} + +void tst_QList::insertMovable() const +{ + insert(); +} + +void tst_QList::insertCustom() const +{ + insert(); +} + +void tst_QList::isEmpty() const +{ + QList myvec; + + // starts ok + QVERIFY(myvec.isEmpty()); + + // not empty now + myvec.append(QLatin1String("hello there")); + QVERIFY(!myvec.isEmpty()); + + // empty again + myvec.remove(0); + QVERIFY(myvec.isEmpty()); +} + +void tst_QList::last() const +{ + QList myvec; + myvec << "A" << "B" << "C"; + + // test starts ok + QCOMPARE(myvec.last(), QLatin1String("C")); + QCOMPARE(myvec.constLast(), QLatin1String("C")); + + // test it changes ok + myvec.append(QLatin1String("X")); + QCOMPARE(myvec.last(), QLatin1String("X")); + QCOMPARE(myvec.constLast(), QLatin1String("X")); + + // and remove again + myvec.remove(3); + QCOMPARE(myvec.last(), QLatin1String("C")); + QCOMPARE(myvec.constLast(), QLatin1String("C")); +} + +void tst_QList::constLast() const +{ + QList myvec; + myvec << 69 << 42 << 3; + + // test it starts ok + QCOMPARE(myvec.constLast(), 3); + QVERIFY(myvec.isDetached()); + + QList myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constLast(), 3); + QCOMPARE(myvecCopy.constLast(), 3); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test removal changes + myvec.removeLast(); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constLast(), 42); + QCOMPARE(myvecCopy.constLast(), 3); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constLast(), 42); + QCOMPARE(myvecCopy.constLast(), 42); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + // test prepend changes + myvec.append(23); + QVERIFY(myvec.isDetached()); + QVERIFY(!myvec.isSharedWith(myvecCopy)); + QCOMPARE(myvec.constLast(), 23); + QCOMPARE(myvecCopy.constLast(), 42); + + myvecCopy = myvec; + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); + + QCOMPARE(myvec.constLast(), 23); + QCOMPARE(myvecCopy.constLast(), 23); + + QVERIFY(!myvec.isDetached()); + QVERIFY(!myvecCopy.isDetached()); + QVERIFY(myvec.isSharedWith(myvecCopy)); + QVERIFY(myvecCopy.isSharedWith(myvec)); +} + +void tst_QList::lastIndexOf() const +{ + QList myvec; + myvec << "A" << "B" << "C" << "B" << "A"; + + QVERIFY(myvec.lastIndexOf("B") == 3); + QVERIFY(myvec.lastIndexOf("B", 2) == 1); + QVERIFY(myvec.lastIndexOf("X") == -1); + QVERIFY(myvec.lastIndexOf("X", 2) == -1); + + // add an X + myvec << "X"; + QVERIFY(myvec.lastIndexOf("X") == 5); + QVERIFY(myvec.lastIndexOf("X", 5) == 5); + QVERIFY(myvec.lastIndexOf("X", 3) == -1); + + // remove first A + myvec.remove(0); + QVERIFY(myvec.lastIndexOf("A") == 3); + QVERIFY(myvec.lastIndexOf("A", 3) == 3); + QVERIFY(myvec.lastIndexOf("A", 2) == -1); +} + +void tst_QList::mid() const +{ + QList list; + list << "foo" << "bar" << "baz" << "bak" << "buck" << "hello" << "kitty"; + + QCOMPARE(list.mid(3, 3), QList() << "bak" << "buck" << "hello"); + QCOMPARE(list.mid(6, 10), QList() << "kitty"); + QCOMPARE(list.mid(-1, 20), list); + QCOMPARE(list.mid(4), QList() << "buck" << "hello" << "kitty"); +} + +template +void tst_QList::qhash() const +{ + QList l1, l2; + QCOMPARE(qHash(l1), qHash(l2)); + l1 << SimpleValue::at(0); + l2 << SimpleValue::at(0); + QCOMPARE(qHash(l1), qHash(l2)); +} + +template +void tst_QList::move() const +{ + QList list; + list << T_FOO << T_BAR << T_BAZ; + + // move an item + list.move(0, list.count() - 1); + QCOMPARE(list, QList() << T_BAR << T_BAZ << T_FOO); + + // move it back + list.move(list.count() - 1, 0); + QCOMPARE(list, QList() << T_FOO << T_BAR << T_BAZ); + + // move an item in the middle + list.move(1, 0); + QCOMPARE(list, QList() << T_BAR << T_FOO << T_BAZ); +} + +void tst_QList::moveInt() const +{ + move(); +} + +void tst_QList::moveMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + move(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::moveCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + move(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +template +void tst_QList::prepend() const +{ + QList myvec; + T val1 = SimpleValue::at(0); + T val2 = SimpleValue::at(1); + T val3 = SimpleValue::at(2); + T val4 = SimpleValue::at(3); + T val5 = SimpleValue::at(4); + myvec << val1 << val2 << val3; + + // starts ok + QVERIFY(myvec.size() == 3); + QCOMPARE(myvec.at(0), val1); + + // add something + myvec.prepend(val4); + QCOMPARE(myvec.at(0), val4); + QCOMPARE(myvec.at(1), val1); + QVERIFY(myvec.size() == 4); + + // something else + myvec.prepend(val5); + QCOMPARE(myvec.at(0), val5); + QCOMPARE(myvec.at(1), val4); + QCOMPARE(myvec.at(2), val1); + QVERIFY(myvec.size() == 5); + + // clear and prepend to an empty vector + myvec.clear(); + QVERIFY(myvec.size() == 0); + myvec.prepend(val5); + QVERIFY(myvec.size() == 1); + QCOMPARE(myvec.at(0), val5); +} + +void tst_QList::prependInt() const +{ + prepend(); +} + +void tst_QList::prependMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + prepend(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::prependCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + prepend(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::removeAllWithAlias() const +{ + QList strings; + strings << "One" << "Two" << "Three" << "One" /* must be distinct, but equal */; + QCOMPARE(strings.removeAll(strings.front()), 2); // will trigger asan/ubsan +} + +template +void tst_QList::remove() const +{ + QList myvec; + T val1 = SimpleValue::at(1); + T val2 = SimpleValue::at(2); + T val3 = SimpleValue::at(3); + T val4 = SimpleValue::at(4); + myvec << val1 << val2 << val3; + myvec << val1 << val2 << val3; + myvec << val1 << val2 << val3; + // remove middle + myvec.remove(1); + QCOMPARE(myvec, QList() << val1 << val3 << val1 << val2 << val3 << val1 << val2 << val3); + + // removeOne() + QVERIFY(!myvec.removeOne(val4)); + QVERIFY(myvec.removeOne(val2)); + QCOMPARE(myvec, QList() << val1 << val3 << val1 << val3 << val1 << val2 << val3); + + QList myvecCopy = myvec; + QVERIFY(myvecCopy.isSharedWith(myvec)); + // removeAll() + QCOMPARE(myvec.removeAll(val4), 0); + QVERIFY(myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec.removeAll(val1), 3); + QVERIFY(!myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec, QList() << val3 << val3 << val2 << val3); + myvecCopy = myvec; + QVERIFY(myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec.removeAll(val2), 1); + QVERIFY(!myvecCopy.isSharedWith(myvec)); + QCOMPARE(myvec, QList() << val3 << val3 << val3); + + // remove rest + myvec.remove(0, 3); + QCOMPARE(myvec, QList()); +} + +void tst_QList::removeInt() const +{ + remove(); +} + +void tst_QList::removeMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + remove(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::removeCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + remove(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +struct RemoveLastTestClass +{ + RemoveLastTestClass() { other = 0; deleted = false; } + RemoveLastTestClass *other; + bool deleted; + ~RemoveLastTestClass() + { + deleted = true; + if (other) + other->other = 0; + } +}; + +void tst_QList::removeFirstLast() const +{ + // pop_pack - pop_front + QList t, t2; + t.append(1); + t.append(2); + t.append(3); + t.append(4); + t2 = t; + t.pop_front(); + QCOMPARE(t.size(), 3); + QCOMPARE(t.at(0), 2); + t.pop_back(); + QCOMPARE(t.size(), 2); + QCOMPARE(t.at(0), 2); + QCOMPARE(t.at(1), 3); + + // takefirst - takeLast + int n1 = t2.takeLast(); + QCOMPARE(t2.size(), 3); + QCOMPARE(n1, 4); + QCOMPARE(t2.at(0), 1); + QCOMPARE(t2.at(2), 3); + n1 = t2.takeFirst(); + QCOMPARE(t2.size(), 2); + QCOMPARE(n1, 1); + QCOMPARE(t2.at(0), 2); + QCOMPARE(t2.at(1), 3); + + // remove first + QList x, y; + x.append(1); + x.append(2); + y = x; + x.removeFirst(); + QCOMPARE(x.size(), 1); + QCOMPARE(y.size(), 2); + QCOMPARE(x.at(0), 2); + + // remove Last + QList v; + v.resize(2); + v[0].other = &(v[1]); + v[1].other = &(v[0]); + // Check dtor - complex type + QVERIFY(v.at(0).other != 0); + v.removeLast(); + QVERIFY(v.at(0).other == 0); + QCOMPARE(v.at(0).deleted, false); + // check iterator + int count = 0; + for (QList::const_iterator i = v.constBegin(); i != v.constEnd(); ++i) { + ++count; + QVERIFY(i->other == 0); + QCOMPARE(i->deleted, false); + } + // Check size + QCOMPARE(count, 1); + QCOMPARE(v.size(), 1); + v.removeLast(); + QCOMPARE(v.size(), 0); + // Check if we do correct realloc + QList v2, v3; + v2.append(1); + v2.append(2); + v3 = v2; // shared + v2.removeLast(); + QCOMPARE(v2.size(), 1); + QCOMPARE(v3.size(), 2); + QCOMPARE(v2.at(0), 1); + QCOMPARE(v3.at(0), 1); + QCOMPARE(v3.at(1), 2); + + // Remove last with shared + QList z1, z2; + z1.append(9); + z2 = z1; + z1.removeLast(); + QCOMPARE(z1.size(), 0); + QCOMPARE(z2.size(), 1); + QCOMPARE(z2.at(0), 9); +} + + +void tst_QList::resizePOD_data() const +{ + QTest::addColumn >("vector"); + QTest::addColumn("size"); + + QVERIFY(!QTypeInfo::isComplex); + QVERIFY(!QTypeInfo::isStatic); + + QList null; + QList empty(0, 5); + QList emptyReserved; + QList nonEmpty; + QList nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << 0 << 1 << 2 << 3 << 4; + nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 10; + QTest::newRow("empty") << empty << 10; + QTest::newRow("emptyReserved") << emptyReserved << 10; + QTest::newRow("nonEmpty") << nonEmpty << 10; + QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; +} + +void tst_QList::resizePOD() const +{ + QFETCH(QList, vector); + QFETCH(int, size); + + const int oldSize = vector.size(); + + vector.resize(size); + QCOMPARE(vector.size(), size); + QVERIFY(vector.capacity() >= size); + for (int i = oldSize; i < size; ++i) + QVERIFY(vector[i] == 0); // check initialization + + const int capacity = vector.capacity(); + + vector.clear(); + QCOMPARE(vector.size(), 0); + QVERIFY(vector.capacity() <= capacity); +} + +void tst_QList::resizeComplexMovable_data() const +{ + QTest::addColumn >("vector"); + QTest::addColumn("size"); + + QVERIFY(QTypeInfo::isComplex); + QVERIFY(!QTypeInfo::isStatic); + + QList null; + QList empty(0, 'Q'); + QList emptyReserved; + QList nonEmpty; + QList nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << '0' << '1' << '2' << '3' << '4'; + nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 10; + QTest::newRow("empty") << empty << 10; + QTest::newRow("emptyReserved") << emptyReserved << 10; + QTest::newRow("nonEmpty") << nonEmpty << 10; + QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; +} + +void tst_QList::resizeComplexMovable() const +{ + const int items = Movable::counter.loadAcquire(); + { + QFETCH(QList, vector); + QFETCH(int, size); + + const int oldSize = vector.size(); + + vector.resize(size); + QCOMPARE(vector.size(), size); + QVERIFY(vector.capacity() >= size); + for (int i = oldSize; i < size; ++i) + QVERIFY(vector[i] == 'j'); // check initialization + + const int capacity = vector.capacity(); + + vector.resize(0); + QCOMPARE(vector.size(), 0); + QVERIFY(vector.capacity() <= capacity); + } + QCOMPARE(items, Movable::counter.loadAcquire()); +} + +void tst_QList::resizeComplex_data() const +{ + QTest::addColumn >("vector"); + QTest::addColumn("size"); + + QVERIFY(QTypeInfo::isComplex); + QVERIFY(QTypeInfo::isStatic); + + QList null; + QList empty(0, '0'); + QList emptyReserved; + QList nonEmpty; + QList nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << '0' << '1' << '2' << '3' << '4'; + nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 10; + QTest::newRow("empty") << empty << 10; + QTest::newRow("emptyReserved") << emptyReserved << 10; + QTest::newRow("nonEmpty") << nonEmpty << 10; + QTest::newRow("nonEmptyReserved") << nonEmptyReserved << 10; +} + +void tst_QList::resizeComplex() const +{ + const int items = Custom::counter.loadAcquire(); + { + QFETCH(QList, vector); + QFETCH(int, size); + + int oldSize = vector.size(); + vector.resize(size); + QCOMPARE(vector.size(), size); + QVERIFY(vector.capacity() >= size); + for (int i = oldSize; i < size; ++i) + QVERIFY(vector[i].i == 'j'); // check default initialization + + const int capacity = vector.capacity(); + + vector.resize(0); + QCOMPARE(vector.size(), 0); + QVERIFY(vector.isEmpty()); + QVERIFY(vector.capacity() <= capacity); + } + QCOMPARE(Custom::counter.loadAcquire(), items); +} + +void tst_QList::resizeCtorAndDtor() const +{ + const int items = Custom::counter.loadAcquire(); + { + QList null; + QList empty(0, '0'); + QList emptyReserved; + QList nonEmpty; + QList nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + nonEmpty << '0' << '1' << '2' << '3' << '4'; + nonEmptyReserved << '0' << '1' << '2' << '3' << '4' << '5' << '6'; + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + // start playing with vectors + null.resize(21); + nonEmpty.resize(2); + emptyReserved.resize(0); + nonEmpty.resize(0); + nonEmptyReserved.resize(2); + } + QCOMPARE(Custom::counter.loadAcquire(), items); +} + +void tst_QList::reverseIterators() const +{ + QList v; + v << 1 << 2 << 3 << 4; + QList vr = v; + std::reverse(vr.begin(), vr.end()); + const QList &cvr = vr; + QVERIFY(std::equal(v.begin(), v.end(), vr.rbegin())); + QVERIFY(std::equal(v.begin(), v.end(), vr.crbegin())); + QVERIFY(std::equal(v.begin(), v.end(), cvr.rbegin())); + QVERIFY(std::equal(vr.rbegin(), vr.rend(), v.begin())); + QVERIFY(std::equal(vr.crbegin(), vr.crend(), v.begin())); + QVERIFY(std::equal(cvr.rbegin(), cvr.rend(), v.begin())); +} + +template +void tst_QList::size() const +{ + // zero size + QList myvec; + QVERIFY(myvec.size() == 0); + + // grow + myvec.append(SimpleValue::at(0)); + QVERIFY(myvec.size() == 1); + myvec.append(SimpleValue::at(1)); + QVERIFY(myvec.size() == 2); + + // shrink + myvec.remove(0); + QVERIFY(myvec.size() == 1); + myvec.remove(0); + QVERIFY(myvec.size() == 0); +} + +void tst_QList::sizeInt() const +{ + size(); +} + +void tst_QList::sizeMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + size(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::sizeCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + size(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +// ::squeeze() is tested in ::capacity(). + +void tst_QList::startsWith() const +{ + QList myvec; + + // empty vector + QVERIFY(!myvec.startsWith(1)); + + // add the one, should work + myvec.prepend(1); + QVERIFY(myvec.startsWith(1)); + + // add something else, fails now + myvec.prepend(3); + QVERIFY(!myvec.startsWith(1)); + + // remove it again :) + myvec.remove(0); + QVERIFY(myvec.startsWith(1)); +} + +template +void tst_QList::swap() const +{ + QList v1, v2; + T val1 = SimpleValue::at(0); + T val2 = SimpleValue::at(1); + T val3 = SimpleValue::at(2); + T val4 = SimpleValue::at(3); + T val5 = SimpleValue::at(4); + T val6 = SimpleValue::at(5); + v1 << val1 << val2 << val3; + v2 << val4 << val5 << val6; + + v1.swap(v2); + QCOMPARE(v1,QList() << val4 << val5 << val6); + QCOMPARE(v2,QList() << val1 << val2 << val3); +} + +void tst_QList::swapInt() const +{ + swap(); +} + +void tst_QList::swapMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + swap(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::swapCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + swap(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::toList() const +{ + QList myvec; + myvec << "A" << "B" << "C"; + + // make sure it converts and doesn't modify the original vector + QCOMPARE(myvec.toList(), QList() << "A" << "B" << "C"); + QCOMPARE(myvec, QList() << "A" << "B" << "C"); +} + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +void tst_QList::toStdVector() const +{ + QList myvec; + myvec << "A" << "B" << "C"; + + std::vector svec = myvec.toStdVector(); + QCOMPARE(svec.at(0), QLatin1String("A")); + QCOMPARE(svec.at(1), QLatin1String("B")); + QCOMPARE(svec.at(2), QLatin1String("C")); + + QCOMPARE(myvec, QList() << "A" << "B" << "C"); +} +#endif + +void tst_QList::value() const +{ + QList myvec; + myvec << "A" << "B" << "C"; + + // valid calls + QCOMPARE(myvec.value(0), QLatin1String("A")); + QCOMPARE(myvec.value(1), QLatin1String("B")); + QCOMPARE(myvec.value(2), QLatin1String("C")); + + // default calls + QCOMPARE(myvec.value(-1), QString()); + QCOMPARE(myvec.value(3), QString()); + + // test calls with a provided default, valid calls + QCOMPARE(myvec.value(0, QLatin1String("default")), QLatin1String("A")); + QCOMPARE(myvec.value(1, QLatin1String("default")), QLatin1String("B")); + QCOMPARE(myvec.value(2, QLatin1String("default")), QLatin1String("C")); + + // test calls with a provided default that will return the default + QCOMPARE(myvec.value(-1, QLatin1String("default")), QLatin1String("default")); + QCOMPARE(myvec.value(3, QLatin1String("default")), QLatin1String("default")); +} + +void tst_QList::testOperators() const +{ + QList myvec; + myvec << "A" << "B" << "C"; + QList myvectwo; + myvectwo << "D" << "E" << "F"; + QList combined; + combined << "A" << "B" << "C" << "D" << "E" << "F"; + + // != + QVERIFY(myvec != myvectwo); + + // + + QCOMPARE(myvec + myvectwo, combined); + QCOMPARE(myvec, QList() << "A" << "B" << "C"); + QCOMPARE(myvectwo, QList() << "D" << "E" << "F"); + + // += + myvec += myvectwo; + QCOMPARE(myvec, combined); + + // == + QVERIFY(myvec == combined); + + // <, >, <=, >= + QVERIFY(!(myvec < combined)); + QVERIFY(!(myvec > combined)); + QVERIFY( myvec <= combined); + QVERIFY( myvec >= combined); + combined.push_back("G"); + QVERIFY( myvec < combined); + QVERIFY(!(myvec > combined)); + QVERIFY( myvec <= combined); + QVERIFY(!(myvec >= combined)); + QVERIFY(combined > myvec); + QVERIFY(combined >= myvec); + + // [] + QCOMPARE(myvec[0], QLatin1String("A")); + QCOMPARE(myvec[1], QLatin1String("B")); + QCOMPARE(myvec[2], QLatin1String("C")); + QCOMPARE(myvec[3], QLatin1String("D")); + QCOMPARE(myvec[4], QLatin1String("E")); + QCOMPARE(myvec[5], QLatin1String("F")); +} + + +int fooCtor; +int fooDtor; + +struct Foo +{ + int *p; + + Foo() { p = new int; ++fooCtor; } + Foo(const Foo &other) { Q_UNUSED(other); p = new int; ++fooCtor; } + + void operator=(const Foo & /* other */) { } + + ~Foo() { delete p; ++fooDtor; } +}; + +void tst_QList::reserve() +{ + fooCtor = 0; + fooDtor = 0; + { + QList a; + a.resize(2); + QCOMPARE(fooCtor, 2); + QList b(a); + b.reserve(1); + QCOMPARE(b.size(), a.size()); + QCOMPARE(fooDtor, 0); + } + QCOMPARE(fooCtor, fooDtor); +} + +// This is a regression test for QTBUG-51758 +void tst_QList::reserveZero() +{ + QList vec; + vec.detach(); + vec.reserve(0); // should not crash + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.squeeze(); + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.reserve(-1); + QCOMPARE(vec.size(), 0); + QCOMPARE(vec.capacity(), 0); + vec.append(42); + QCOMPARE(vec.size(), 1); + QVERIFY(vec.capacity() >= 1); +} + +template +void tst_QList::initializeList() +{ + T val1(SimpleValue::at(1)); + T val2(SimpleValue::at(2)); + T val3(SimpleValue::at(3)); + T val4(SimpleValue::at(4)); + + QList v1 {val1, val2, val3}; + QCOMPARE(v1, QList() << val1 << val2 << val3); + QCOMPARE(v1, (QList {val1, val2, val3})); + + QList> v2{ v1, {val4}, QList(), {val1, val2, val3} }; + QList> v3; + v3 << v1 << (QList() << val4) << QList() << v1; + QCOMPARE(v3, v2); + + QList v4({}); + QCOMPARE(v4.size(), 0); +} + +void tst_QList::initializeListInt() +{ + initializeList(); +} + +void tst_QList::initializeListMovable() +{ + const int instancesCount = Movable::counter.loadAcquire(); + initializeList(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::initializeListCustom() +{ + const int instancesCount = Custom::counter.loadAcquire(); + initializeList(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +void tst_QList::const_shared_null() +{ + QList v2; + QVERIFY(!v2.isDetached()); +} + +template +void tst_QList::detach() const +{ + { + // detach an empty vector + QList v; + v.detach(); + QVERIFY(!v.isDetached()); + QCOMPARE(v.size(), 0); + QCOMPARE(v.capacity(), 0); + } + { + // detach an empty referenced vector + QList v; + QList ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(!v.isDetached()); + QCOMPARE(v.size(), 0); + QCOMPARE(v.capacity(), 0); + } + { + // detach a not empty referenced vector + QList v(31); + QList ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 31); + QCOMPARE(v.capacity(), 31); + } + { + // detach a not empty vector + QList v(31); + QVERIFY(v.isDetached()); + v.detach(); // detaching a detached vector + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 31); + QCOMPARE(v.capacity(), 31); + } + { + // detach a not empty vector with preallocated space + QList v(3); + v.reserve(8); + QList ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 3); + QCOMPARE(v.capacity(), 8); + } + { + // detach a not empty vector with preallocated space + QList v(3); + v.reserve(8); + QVERIFY(v.isDetached()); + v.detach(); // detaching a detached vector + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 3); + QCOMPARE(v.capacity(), 8); + } + { + // detach a not empty, initialized vector + QList v(7, SimpleValue::at(1)); + QList ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 7); + for (int i = 0; i < v.size(); ++i) + QCOMPARE(v[i], SimpleValue::at(1)); + } + { + // detach a not empty, initialized vector + QList v(7, SimpleValue::at(2)); + QVERIFY(v.isDetached()); + v.detach(); // detaching a detached vector + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 7); + for (int i = 0; i < v.size(); ++i) + QCOMPARE(v[i], SimpleValue::at(2)); + } + { + // detach a not empty, initialized vector with preallocated space + QList v(7, SimpleValue::at(3)); + v.reserve(31); + QList ref(v); + QVERIFY(!v.isDetached()); + v.detach(); + QVERIFY(v.isDetached()); + QCOMPARE(v.size(), 7); + QCOMPARE(v.capacity(), 31); + for (int i = 0; i < v.size(); ++i) + QCOMPARE(v[i], SimpleValue::at(3)); + } +} + +void tst_QList::detachInt() const +{ + detach(); +} + +void tst_QList::detachMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + detach(); + QCOMPARE(instancesCount, Movable::counter.loadAcquire()); +} + +void tst_QList::detachCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + detach(); + QCOMPARE(instancesCount, Custom::counter.loadAcquire()); +} + +static QAtomicPointer > detachThreadSafetyDataInt; +static QAtomicPointer > detachThreadSafetyDataMovable; +static QAtomicPointer > detachThreadSafetyDataCustom; + +template QAtomicPointer > *detachThreadSafetyData(); +template<> QAtomicPointer > *detachThreadSafetyData() { return &detachThreadSafetyDataInt; } +template<> QAtomicPointer > *detachThreadSafetyData() { return &detachThreadSafetyDataMovable; } +template<> QAtomicPointer > *detachThreadSafetyData() { return &detachThreadSafetyDataCustom; } + +static QSemaphore detachThreadSafetyLock; + +template +void tst_QList::detachThreadSafety() const +{ + delete detachThreadSafetyData()->fetchAndStoreOrdered(new QList(SimpleValue::vector(400))); + + static const uint threadsCount = 5; + + struct : QThread { + void run() override + { + QList copy(*detachThreadSafetyData()->loadRelaxed()); + QVERIFY(!copy.isDetached()); + detachThreadSafetyLock.release(); + detachThreadSafetyLock.acquire(100); + copy.detach(); + } + } threads[threadsCount]; + + for (uint i = 0; i < threadsCount; ++i) + threads[i].start(); + QThread::yieldCurrentThread(); + detachThreadSafetyLock.acquire(threadsCount); + + // destroy static original data + delete detachThreadSafetyData()->fetchAndStoreOrdered(0); + + QVERIFY(threadsCount < 100); + detachThreadSafetyLock.release(threadsCount * 100); + QThread::yieldCurrentThread(); + + for (uint i = 0; i < threadsCount; ++i) + threads[i].wait(); +} + +void tst_QList::detachThreadSafetyInt() const +{ + for (uint i = 0; i < 128; ++i) + detachThreadSafety(); +} + +void tst_QList::detachThreadSafetyMovable() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + for (uint i = 0; i < 128; ++i) { + detachThreadSafety(); + QCOMPARE(Movable::counter.loadAcquire(), instancesCount); + } +} + +void tst_QList::detachThreadSafetyCustom() const +{ + const int instancesCount = Custom::counter.loadAcquire(); + for (uint i = 0; i < 128; ++i) { + detachThreadSafety(); + QCOMPARE(Custom::counter.loadAcquire(), instancesCount); + } +} + +void tst_QList::insertMove() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + { + QList vec; + vec.reserve(7); + Movable m0; + Movable m1; + Movable m2; + Movable m3; + Movable m4; + Movable m5; + Movable m6; + + vec.append(std::move(m3)); + QVERIFY(m3.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + vec.push_back(std::move(m4)); + QVERIFY(m4.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + QVERIFY(vec.at(1).wasConstructedAt(&m4)); + vec.prepend(std::move(m1)); + QVERIFY(m1.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m3)); + QVERIFY(vec.at(2).wasConstructedAt(&m4)); + vec.insert(1, std::move(m2)); + QVERIFY(m2.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + QVERIFY(vec.at(3).wasConstructedAt(&m4)); + vec += std::move(m5); + QVERIFY(m5.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + QVERIFY(vec.at(3).wasConstructedAt(&m4)); + QVERIFY(vec.at(4).wasConstructedAt(&m5)); + vec << std::move(m6); + QVERIFY(m6.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + QVERIFY(vec.at(3).wasConstructedAt(&m4)); + QVERIFY(vec.at(4).wasConstructedAt(&m5)); + QVERIFY(vec.at(5).wasConstructedAt(&m6)); + vec.push_front(std::move(m0)); + QVERIFY(m0.wasConstructedAt(nullptr)); + QVERIFY(vec.at(0).wasConstructedAt(&m0)); + QVERIFY(vec.at(1).wasConstructedAt(&m1)); + QVERIFY(vec.at(2).wasConstructedAt(&m2)); + QVERIFY(vec.at(3).wasConstructedAt(&m3)); + QVERIFY(vec.at(4).wasConstructedAt(&m4)); + QVERIFY(vec.at(5).wasConstructedAt(&m5)); + QVERIFY(vec.at(6).wasConstructedAt(&m6)); + + QCOMPARE(Movable::counter.loadAcquire(), instancesCount + 14); + } + QCOMPARE(Movable::counter.loadAcquire(), instancesCount); +} + +void tst_QList::swapItemsAt() const +{ + QList v; + v << 0 << 1 << 2 << 3; + + v.swapItemsAt(0, 2); + QCOMPARE(v.at(0), 2); + QCOMPARE(v.at(2), 0); + + auto copy = v; + copy.swapItemsAt(0, 2); + QCOMPARE(v.at(0), 2); + QCOMPARE(v.at(2), 0); + QCOMPARE(copy.at(0), 0); + QCOMPARE(copy.at(2), 2); +} + +void tst_QList::emplaceInt() +{ + emplaceImpl(); +} + +void tst_QList::emplaceCustom() +{ + emplaceImpl(); +} + +void tst_QList::emplaceMovable() +{ + emplaceImpl(); +} + +void tst_QList::emplaceConsistentWithStdVectorInt() +{ + emplaceConsistentWithStdVectorImpl(); +} + +void tst_QList::emplaceConsistentWithStdVectorCustom() +{ + emplaceConsistentWithStdVectorImpl(); +} + +void tst_QList::emplaceConsistentWithStdVectorMovable() +{ + emplaceConsistentWithStdVectorImpl(); +} + +void tst_QList::emplaceReturnsIterator() +{ + QList vec; + + vec.emplace(0, 'k')->i = 'p'; + + QCOMPARE(vec[0].i, 'p'); +} + +void tst_QList::emplaceBack() +{ + QScopedValueRollback rollback(Movable::counter, 0); + + QList vec; + + vec.emplaceBack('k'); + + QCOMPARE(Movable::counter, 1); +} + +void tst_QList::emplaceBackReturnsRef() +{ + QList vec; + + vec.emplaceBack('k').i = 'p'; + + QCOMPARE(vec.at(0).i, 'p'); +} + +void tst_QList::emplaceWithElementFromTheSameContainer() +{ + QFETCH(int, elementPos); + QFETCH(int, insertPos); + QFETCH(bool, doCopy); + + QList vec {"a", "b", "c", "d", "e"}; + const QString e = vec[elementPos]; + + if (doCopy) + vec.emplace(insertPos, vec[elementPos]); + else + vec.emplace(insertPos, std::move(vec[elementPos])); + + QCOMPARE(vec[insertPos], e); +} + +void tst_QList::emplaceWithElementFromTheSameContainer_data() +{ + QTest::addColumn("elementPos"); + QTest::addColumn("insertPos"); + QTest::addColumn("doCopy"); + + for (int i = 0; i < 2; ++i) { + const bool doCopy = i == 0; + const char *opName = doCopy ? "copy" : "move"; + + QTest::addRow("%s: begin -> end" , opName) << 0 << 5 << doCopy; + QTest::addRow("%s: begin -> middle", opName) << 0 << 2 << doCopy; + QTest::addRow("%s: middle -> begin" , opName) << 2 << 0 << doCopy; + QTest::addRow("%s: middle -> end" , opName) << 2 << 5 << doCopy; + QTest::addRow("%s: end -> middle", opName) << 4 << 2 << doCopy; + QTest::addRow("%s: end -> begin" , opName) << 4 << 0 << doCopy; + } +} + +template +void tst_QList::emplaceImpl() const +{ + QList vec {'a', 'b', 'c', 'd'}; + + vec.emplace(2, 'k'); + + QCOMPARE(vec[2], T('k')); +} + +template +static void vecEq(const QList &qVec, const std::vector &stdVec) +{ + QCOMPARE(std::size_t(qVec.size()), stdVec.size()); + QVERIFY(std::equal(qVec.begin(), qVec.end(), stdVec.begin(), stdVec.end())); +} + +template +static void squeezeVec(QList &qVec, std::vector &stdVec) +{ + qVec.squeeze(); + stdVec.shrink_to_fit(); +} + +template +void tst_QList::emplaceConsistentWithStdVectorImpl() const +{ + QList qVec {'a', 'b', 'c', 'd', 'e'}; + std::vector stdVec {'a', 'b', 'c', 'd', 'e'}; + vecEq(qVec, stdVec); + + qVec.emplaceBack('f'); + stdVec.emplace_back('f'); + vecEq(qVec, stdVec); + + qVec.emplace(3, 'g'); + stdVec.emplace(stdVec.begin() + 3, 'g'); + vecEq(qVec, stdVec); + + T t; + // while QVector is safe with regards to emplacing elements moved form itself, it's UB + // for std::vector, so do the moving in two steps there. + qVec.emplaceBack(std::move(qVec[0])); + stdVec.emplace_back(std::move(t = std::move(stdVec[0]))); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplaceBack(std::move(qVec[1])); + stdVec.emplace_back(std::move(t = std::move(stdVec[1]))); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplace(3, std::move(qVec[5])); + stdVec.emplace(stdVec.begin() + 3, std::move(t = std::move(stdVec[5]))); + + vecEq(qVec, stdVec); + + qVec.emplaceBack(qVec[3]); + stdVec.emplace_back((t = stdVec[3])); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplaceBack(qVec[4]); + stdVec.emplace_back((t = stdVec[4])); + vecEq(qVec, stdVec); + + squeezeVec(qVec, stdVec); + + qVec.emplace(5, qVec[7]); + stdVec.emplace(stdVec.begin() + 5, (t = stdVec[7])); + vecEq(qVec, stdVec); +} + +QTEST_MAIN(tst_QList) +#include "tst_qlist.moc" -- cgit v1.2.3