diff options
Diffstat (limited to 'tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp')
-rw-r--r-- | tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp | 700 |
1 files changed, 662 insertions, 38 deletions
diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index d7f43fdeca..5eb9dbfa36 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -1,49 +1,43 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> -** 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 <QtTest/QtTest> +// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include "qbytearray.h" #include "qdebug.h" #include "qhash.h" +#include "qmap.h" +#include "qset.h" #include "qlist.h" #include "qstring.h" #include "qvarlengtharray.h" #include <algorithm> #include <functional> -#include <vector> // for reference +#include <iostream> #include <list> #include <set> +#include <sstream> #include <map> #include <forward_list> #include <unordered_set> #include <unordered_map> +#include <q20vector.h> // For reference + +QT_BEGIN_NAMESPACE +std::ostream &operator<<(std::ostream &os, const QChar &c) +{ + Q_ASSERT(c == QLatin1Char{c.toLatin1()}); + return os << c.toLatin1(); +} +std::istream &operator>>(std::istream &os, QChar &c) +{ + char cL1; + os >> cL1; + c = QLatin1Char{cL1}; + return os; +} +QT_END_NAMESPACE struct Movable { @@ -66,6 +60,11 @@ struct Movable int i; static int instanceCount; + + friend std::ostream &operator<<(std::ostream &os, const Movable &m) + { return os << m.i; } + friend std::istream &operator>>(std::istream &os, Movable &m) + { return os >> m.i; } }; int Movable::instanceCount = 0; @@ -81,7 +80,7 @@ QDebug &operator<<(QDebug &d, Movable m) } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Movable, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE struct Complex @@ -102,9 +101,16 @@ struct Complex { --instanceCount; } + constexpr Complex &operator=(const Complex &o) noexcept + { i = o.i; return *this; } int i; static int instanceCount; + + friend std::ostream &operator<<(std::ostream &os, const Complex &c) + { return os << c.i; } + friend std::istream &operator>>(std::istream &os, Complex &c) + { return os >> c.i; } }; int Complex::instanceCount = 0; @@ -312,6 +318,49 @@ private Q_SLOTS: private: template <typename Container> + void resize_impl() const; + +private Q_SLOTS: + void resize_std_vector() { resize_impl<std::vector<int>>(); } + void resize_QList() { resize_impl<QList<qintptr>>(); } + void resize_QVarLengthArray() { resize_impl<QVarLengthArray<int>>(); } + void resize_QString() { resize_impl<QString>(); } + void resize_QByteArray() { resize_impl<QByteArray>(); } + +private: + template <typename Container> + void copesWithValueTypesWithConstMembers_impl(); + + struct ConstMember { + #ifndef __cpp_aggregate_paren_init // also check that we can emplace aggregates (C++20 only) + explicit ConstMember(int n) : n(n) {} + #endif + const int n; + + friend bool operator==(const ConstMember &lhs, const ConstMember &rhs) noexcept + { return lhs.n == rhs.n; } + friend bool operator!=(const ConstMember &lhs, const ConstMember &rhs) noexcept + { return !(lhs == rhs); } + }; + +private Q_SLOTS: + void copesWithValueTypesWithConstMembers_std_vector() { copesWithValueTypesWithConstMembers_impl<std::vector<ConstMember>>(); } + void copesWithValueTypesWithConstMembers_QVarLengthArray() { copesWithValueTypesWithConstMembers_impl<QVarLengthArray<ConstMember, 2>>(); } + +private: + template <typename Container> + void assign_impl() const; + +private Q_SLOTS: + void assign_std_vector() { assign_impl<std::vector<int>>(); }; + void assign_std_string() { assign_impl<std::string>(); } + void assign_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); }; + void assign_QList() { assign_impl<QList<int>>(); } + void assign_QByteArray() { assign_impl<QByteArray>(); } + void assign_QString() { assign_impl<QString>(); } + +private: + template <typename Container> void front_back_impl() const; private Q_SLOTS: @@ -322,6 +371,64 @@ private Q_SLOTS: void front_back_QStringView() { front_back_impl<QStringView>(); } void front_back_QLatin1String() { front_back_impl<QLatin1String>(); } void front_back_QByteArray() { front_back_impl<QByteArray>(); } + +private: + template <typename Container> + void erase_impl() const; + + template <typename Container> + void erase_if_impl() const; + + template <typename Container> + void erase_if_associative_impl() const; + + template <typename Container> + void member_erase_impl() const; + + template <typename Container> + void member_erase_associative_impl() const; + + template <typename Container> + void member_erase_set_impl() const; + +private Q_SLOTS: + void erase_QList() { erase_impl<QList<int>>(); } + void erase_QVarLengthArray() { erase_impl<QVarLengthArray<int>>(); } + void erase_QString() { erase_impl<QString>(); } + void erase_QByteArray() { erase_impl<QByteArray>(); } + void erase_std_vector() { erase_impl<std::vector<int>>(); } + + void erase_if_QList() { erase_if_impl<QList<int>>(); } + void erase_if_QVarLengthArray() { erase_if_impl<QVarLengthArray<int>>(); } + void erase_if_QSet() { erase_if_impl<QSet<int>>(); } + void erase_if_QString() { erase_if_impl<QString>(); } + void erase_if_QByteArray() { erase_if_impl<QByteArray>(); } + void erase_if_std_vector() { erase_if_impl<std::vector<int>>(); } + void erase_if_QMap() { erase_if_associative_impl<QMap<int, int>>(); } + void erase_if_QMultiMap() {erase_if_associative_impl<QMultiMap<int, int>>(); } + void erase_if_QHash() { erase_if_associative_impl<QHash<int, int>>(); } + void erase_if_QMultiHash() { erase_if_associative_impl<QMultiHash<int, int>>(); } + + void member_erase_QList() { member_erase_impl<QList<int>>(); } + void member_erase_QVarLengthArray() { member_erase_impl<QVarLengthArray<int>>(); } + void member_erase_QString() { member_erase_impl<QString>(); } + void member_erase_QByteArray() { member_erase_impl<QByteArray>(); } + void member_erase_QSet() { member_erase_set_impl<QSet<int>>(); } + + void member_erase_QMap() { member_erase_associative_impl<QMap<int, int>>(); } + void member_erase_QMultiMap() {member_erase_associative_impl<QMultiMap<int, int>>(); } + void member_erase_QHash() { member_erase_associative_impl<QHash<int, int>>(); } + void member_erase_QMultiHash() { member_erase_associative_impl<QMultiHash<int, int>>(); } + +private: + template <typename Container> + void keyValueRange_impl() const; + +private Q_SLOTS: + void keyValueRange_QMap() { keyValueRange_impl<QMap<int, int>>(); } + void keyValueRange_QMultiMap() { keyValueRange_impl<QMultiMap<int, int>>(); } + void keyValueRange_QHash() { keyValueRange_impl<QHash<int, int>>(); } + void keyValueRange_QMultiHash() { keyValueRange_impl<QMultiHash<int, int>>(); } }; void tst_ContainerApiSymmetry::init() @@ -380,12 +487,25 @@ void tst_ContainerApiSymmetry::ranged_ctor_non_associative_impl() const // from itself const Container c4(reference.begin(), reference.end()); + // from stringsteam (= pure input_iterator) + const Container c5 = [&] { + { + std::stringstream ss; + for (auto &v : values1) + ss << v << ' '; + ss.seekg(0); + return Container(std::istream_iterator<V>{ss}, + std::istream_iterator<V>{}); + } + }(); + QCOMPARE(c1, reference); QCOMPARE(c2a, reference); QCOMPARE(c2b, reference); QCOMPARE(c3a, reference); QCOMPARE(c3b, reference); QCOMPARE(c4, reference); + QCOMPARE(c5, reference); } @@ -608,35 +728,539 @@ template <typename Container> Container make(int size) { Container c; - int i = 1; - while (size--) - c.push_back(typename Container::value_type(i++)); + c.reserve(size); + using V = typename Container::value_type; + int i = 0; + std::generate_n(std::inserter(c, c.end()), size, [&i] { return V(++i); }); + return c; +} + +template <typename Container> +Container makeAssociative(int size) +{ + using K = typename Container::key_type; + using V = typename Container::mapped_type; + Container c; + for (int i = 1; i <= size; ++i) + c.insert(K(i), V(i)); return c; } static QString s_string = QStringLiteral("\1\2\3\4\5\6\7"); +template <> QString make(int size) { return s_string.left(size); } template <> QStringView make(int size) { return QStringView(s_string).left(size); } template <> QLatin1String make(int size) { return QLatin1String("\1\2\3\4\5\6\7", size); } +template <> QByteArray make(int size) { return QByteArray("\1\2\3\4\5\6\7", size); } template <typename T> T clean(T &&t) { return std::forward<T>(t); } inline char clean(QLatin1Char ch) { return ch.toLatin1(); } template <typename Container> +void tst_ContainerApiSymmetry::resize_impl() const +{ + using V = typename Container::value_type; + using S = typename Container::size_type; + auto c = make<Container>(3); + QCOMPARE(c.size(), S(3)); + c.resize(4, V(5)); + QCOMPARE(std::size(c), S(4)); + QCOMPARE(c.back(), V(5)); + + // ctor/resize symmetry: + { + Container c1(S(5), V(4)); + QCOMPARE(c1.size(), S(5)); + + Container c2; + c2.resize(S(5), V(4)); + QCOMPARE(c2.size(), S(5)); + + QCOMPARE(c1, c2); + } +} + +template <typename T> +[[maybe_unused]] +constexpr bool is_vector_v = false; +template <typename...Args> +constexpr bool is_vector_v<std::vector<Args...>> = true; + +template <typename Container, typename Value> +void wrap_resize(Container &c, typename Container::size_type n, const Value &v) +{ +#ifdef __GLIBCXX__ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 + if constexpr (is_vector_v<Container>) { + while (c.size() < n) + c.push_back(v); + } else +#endif + { + c.resize(n, v); + } +} + +template <typename Container> +void tst_ContainerApiSymmetry::copesWithValueTypesWithConstMembers_impl() +{ + // The problem: + // + // using V = ConstMember; + // V v{42}; + // assert(v.n == 42); // OK + // new (&v) V{24}; + // assert(v.n == 24); // UB in C++17: v.n could still be 42 (C++17 [basic.life]/8) + // // OK in C++20 (C++20 [basic.life]/8) + // assert(std::launder(&v)->n == 24); // OK + // assert(v.n == 24); // _still_ UB! + // + // Containers: + // - must not expose this problem + // - must compile in the first place, even though V + // - is not assignable + // - is not default-constructible + + using S = typename Container::size_type; + using V = typename Container::value_type; + + Container c; + // the following are all functions that by rights should not require the type to be + // - default-constructible + // - assignable + // make sure they work + c.reserve(S(5)); + c.shrink_to_fit(); + wrap_resize(c, 1, V(42)); + QCOMPARE(c[0], V(42)); + wrap_resize(c, 2, V(48)); + QCOMPARE(c[0], V(42)); + QCOMPARE(c[1], V(48)); + c.clear(); + c.emplace_back(24); + QCOMPARE(c.front(), V(24)); + c.push_back(V(41)); + QCOMPARE(c.back(), V(41)); + { + const auto v142 = V(142); + c.push_back(v142); + } + QCOMPARE(c.size(), S(3)); + QCOMPARE(c[0], V(24)); + QCOMPARE(c[1], V(41)); + QCOMPARE(c[2], V(142)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::assign_impl() const +{ +#define CHECK(Arr, ComparisonData, Sz_n, Sz_e) \ + QCOMPARE(Sz_n, Sz_e); \ + for (const auto &e : Arr) \ + QCOMPARE(e, ComparisonData) \ + /*end*/ +#define RET_CHECK(...) \ + do { \ + if constexpr (std::is_void_v<decltype( __VA_ARGS__ )>) { \ + /* e.g. std::vector */ \ + __VA_ARGS__ ; \ + } else { \ + /* e.g. std::basic_string */ \ + auto &&r = __VA_ARGS__ ; \ + QCOMPARE_EQ(&r, &c); \ + } \ + } while (false) \ + /* end */ + using V = typename Container::value_type; + using S = typename Container::size_type; + auto tData = V(65); + { + // fill version + auto c = make<Container>(4); + const S oldCapacity = c.capacity(); + RET_CHECK(c.assign(4, tData)); + CHECK(c, tData, c.size(), S(4)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + + tData = V(66); + c.assign(8, tData); // may reallocate + CHECK(c, tData, c.size(), S(8)); + + const S grownCapacity = c.capacity(); + c.assign(0, tData); + CHECK(c, tData, c.size(), S(0)); + QCOMPARE_EQ(c.capacity(), grownCapacity); + } + { + // range version for non input iterator + auto c = make<Container>(4); + auto iter = make<Container>(1); + + iter.assign(8, tData); + RET_CHECK(c.assign(iter.begin(), iter.end())); // may reallocate + CHECK(c, tData, c.size(), S(8)); + + const S oldCapacity = c.capacity(); + c.assign(iter.begin(), iter.begin()); + CHECK(c, tData, c.size(), S(0)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + } + { + // range version for input iterator + auto c = make<Container>(4); + const S oldCapacity = c.capacity(); + + std::stringstream ss; + ss << tData << ' ' << tData << ' '; + RET_CHECK(c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{})); + CHECK(c, tData, c.size(), S(2)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + + ss.str(""); + ss.clear(); + tData = V(66); + ss << tData << ' ' << tData << ' ' << tData << ' ' << tData << ' '; + c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{}); + CHECK(c, tData, c.size(), S(4)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + + ss.str(""); + ss.clear(); + tData = V(67); + ss << tData << ' ' << tData << ' ' << tData << ' ' << tData << ' ' + << tData << ' ' << tData << ' ' << tData << ' '; + c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{}); // may reallocate + CHECK(c, tData, c.size(), S(7)); + } + { + // initializer-list version + auto c = make<Container>(4); + const S oldCapacity = c.capacity(); + std::initializer_list<V> list = {tData, tData, tData}; + RET_CHECK(c.assign(list)); + CHECK(c, tData, c.size(), S(3)); + QCOMPARE_EQ(c.capacity(), oldCapacity); + } + +#undef RET_CHECK +#undef CHECK +} + +template<typename Container> void tst_ContainerApiSymmetry::front_back_impl() const { using V = typename Container::value_type; auto c1 = make<Container>(1); QCOMPARE(clean(c1.front()), V(1)); QCOMPARE(clean(c1.back()), V(1)); - QCOMPARE(clean(qAsConst(c1).front()), V(1)); - QCOMPARE(clean(qAsConst(c1).back()), V(1)); + QCOMPARE(clean(std::as_const(c1).front()), V(1)); + QCOMPARE(clean(std::as_const(c1).back()), V(1)); auto c2 = make<Container>(2); QCOMPARE(clean(c2.front()), V(1)); QCOMPARE(clean(c2.back()), V(2)); - QCOMPARE(clean(qAsConst(c2).front()), V(1)); - QCOMPARE(clean(qAsConst(c2).back()), V(2)); + QCOMPARE(clean(std::as_const(c2).front()), V(1)); + QCOMPARE(clean(std::as_const(c2).back()), V(2)); +} + +namespace { +struct Conv { + template <typename T> + static int toInt(T i) { return i; } + static int toInt(QChar ch) { return ch.unicode(); } +}; +} + +template <typename Container> +void tst_ContainerApiSymmetry::erase_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::value_type; + auto c = make<Container>(7); // {1, 2, 3, 4, 5, 6, 7} + QCOMPARE(c.size(), S(7)); + + using q20::erase; // For std::vector + auto result = erase(c, V(1)); + QCOMPARE(result, S(1)); + QCOMPARE(c.size(), S(6)); + + result = erase(c, V(5)); + QCOMPARE(result, S(1)); + QCOMPARE(c.size(), S(5)); + + result = erase(c, V(123)); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(5)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::erase_if_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::value_type; + auto c = make<Container>(7); // {1, 2, 3, 4, 5, 6, 7} + QCOMPARE(c.size(), S(7)); + + decltype(c.size()) oldSize, count; + + oldSize = c.size(); + count = 0; + + using q20::erase_if; // For std::vector + + S result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 2 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(4)); + QCOMPARE(count, oldSize); + + oldSize = c.size(); + count = 0; + result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 123 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(4)); + QCOMPARE(count, oldSize); + + oldSize = c.size(); + count = 0; + result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 3 == 0; }); + QCOMPARE(result, S(1)); + QCOMPARE(c.size(), S(3)); + QCOMPARE(count, oldSize); + + oldSize = c.size(); + count = 0; + result = erase_if(c, [&](V i) { ++count; return Conv::toInt(i) % 2 == 1; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(0)); + QCOMPARE(count, oldSize); +} + +template <typename Container> +void tst_ContainerApiSymmetry::erase_if_associative_impl() const +{ + using S = typename Container::size_type; + using K = typename Container::key_type; + using V = typename Container::mapped_type; + using I = typename Container::iterator; + using P = std::pair<const K &, V &>; + + auto c = makeAssociative<Container>(20); + QCOMPARE(c.size(), S(20)); + + auto result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 0; }); + QCOMPARE(result, S(10)); + QCOMPARE(c.size(), S(10)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 3 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 42 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 1; }); + QCOMPARE(result, S(7)); + QCOMPARE(c.size(), S(0)); + + // same, but with a predicate taking a Qt iterator + c = makeAssociative<Container>(20); + QCOMPARE(c.size(), S(20)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 0; }); + QCOMPARE(result, S(10)); + QCOMPARE(c.size(), S(10)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 3 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 42 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 1; }); + QCOMPARE(result, S(7)); + QCOMPARE(c.size(), S(0)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::member_erase_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::value_type; + const S size = 7; + auto c = make<Container>(size); // {1, 2, 3, 4, 5, 6, 7} + QCOMPARE(c.size(), size); + + auto copy = c; + // Container::erase() returns an iterator, not const_iterator + auto it = c.erase(c.cbegin(), c.cbegin()); + static_assert(std::is_same_v<decltype(it), typename Container::iterator>); + QCOMPARE(c.size(), size); + const V newVal{100}; + QCOMPARE_NE(*it, newVal); + *it = newVal; + QCOMPARE(it, c.cbegin()); + QCOMPARE(*c.cbegin(), newVal); + + QCOMPARE(std::find(copy.cbegin(), copy.cend(), newVal), copy.cend()); +} + +template <typename Container> +void tst_ContainerApiSymmetry::member_erase_associative_impl() const +{ + using S = typename Container::size_type; + using V = typename Container::mapped_type; + + const S size = 20; + auto c = makeAssociative<Container>(size); + QCOMPARE(c.size(), size); + + // Verify Container::erase() returns iterator, not const_iterator + auto it = c.erase(c.cbegin()); + static_assert(std::is_same_v<decltype(it), typename Container::iterator>); + QCOMPARE(c.size(), size - 1); + QCOMPARE(it, c.cbegin()); + const auto current = it.value(); + it.value() = current + V(5); + QCOMPARE(c.cbegin().value(),current + V(5)); +} + +template <typename Container> +void tst_ContainerApiSymmetry::member_erase_set_impl() const +{ + using S = typename Container::size_type; + + const S size = 20; + auto c = make<Container>(size); + QCOMPARE(c.size(), size); + + // Verify Container::erase() returns iterator, not const_iterator + auto it = c.erase(c.cbegin()); + static_assert(std::is_same_v<decltype(it), typename Container::iterator>); + QCOMPARE(c.size(), size - 1); + QCOMPARE(it, c.cbegin()); +} + +template <typename Container> +void tst_ContainerApiSymmetry::keyValueRange_impl() const +{ + constexpr int COUNT = 20; + + using K = typename Container::key_type; + using V = typename Container::mapped_type; + QVector<K> keys; + keys.reserve(COUNT); + QVector<V> values; + values.reserve(COUNT); + + auto c = makeAssociative<Container>(COUNT); + auto returnC = [&](){ return c; }; + + const auto verify = [](QVector<K> v, int count, int offset = 0) -> bool { + if (v.size() != count) + return false; + std::sort(v.begin(), v.end()); + for (int i = 0; i < count; ++i) { + // vector is indexed from 0, but makeAssociative starts from 1 + if (v[i] != i + 1 + offset) + return false; + } + return true; + }; + + // Check that the range has the right size + auto range = c.asKeyValueRange(); + QCOMPARE(std::distance(range.begin(), range.end()), COUNT); + + auto constRange = std::as_const(c).asKeyValueRange(); + QCOMPARE(std::distance(constRange.begin(), constRange.end()), COUNT); + + auto rvalueRange = returnC().asKeyValueRange(); + QCOMPARE(std::distance(rvalueRange.begin(), rvalueRange.end()), COUNT); + + // auto, mutating + keys.clear(); values.clear(); + for (auto [key, value] : c.asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value); + QCOMPARE(c.value(key), value); + ++value; + QCOMPARE(key, value - 1); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT)); + + // auto, non-mutating + keys.clear(); values.clear(); + for (auto [key, value] : c.asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 1); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 1)); + + // auto &&, mutating + keys.clear(); values.clear(); + for (auto &&[key, value] : c.asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 1); + QCOMPARE(c.value(key), value); + ++value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 1)); + + // auto, non-mutating (const map) + keys.clear(); values.clear(); + for (auto [key, value] : std::as_const(c).asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); + + // auto &&, non-mutating (const map) + keys.clear(); values.clear(); + for (auto &&[key, value] : std::as_const(c).asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); + + // auto, non-mutating (rvalue map) + keys.clear(); values.clear(); + for (auto [key, value] : returnC().asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); + + // auto &&, non-mutating (rvalue map) + keys.clear(); values.clear(); + for (auto &&[key, value] : returnC().asKeyValueRange()) { + keys << key; + values << value; + QCOMPARE(key, value - 2); + QCOMPARE(c.value(key), value); + } + QVERIFY(verify(keys, COUNT)); + QVERIFY(verify(values, COUNT, 2)); } QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry) |