summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp')
-rw-r--r--tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp894
1 files changed, 680 insertions, 214 deletions
diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
index 733d8432bb..5eb9dbfa36 100644
--- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
+++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
@@ -1,66 +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>
-
-// MSVC has these containers from the Standard Library, but it lacks
-// a __has_include mechanism (that we need to use for other stdlibs).
-// For the sake of increasing our test coverage, work around the issue.
-
-#ifdef Q_CC_MSVC
-#define COMPILER_HAS_STDLIB_INCLUDE(x) 1
-#else
-#define COMPILER_HAS_STDLIB_INCLUDE(x) __has_include(x)
-#endif
-
-#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
#include <forward_list>
-#endif
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
#include <unordered_set>
-#endif
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
#include <unordered_map>
-#endif
+#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
{
@@ -83,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;
@@ -98,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
@@ -119,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;
@@ -222,20 +211,7 @@ template<typename T>
class VarLengthArray : public QVarLengthArray<T>
{
public:
-#ifdef Q_COMPILER_INHERITING_CONSTRUCTORS
using QVarLengthArray<T>::QVarLengthArray;
-#else
- template<typename InputIterator>
- VarLengthArray(InputIterator first, InputIterator last)
- : QVarLengthArray<T>(first, last)
- {
- }
-
- VarLengthArray(std::initializer_list<T> args)
- : QVarLengthArray<T>(args)
- {
- }
-#endif
};
class tst_ContainerApiSymmetry : public QObject
@@ -285,37 +261,10 @@ private Q_SLOTS:
void ranged_ctor_std_list_Complex() { ranged_ctor_non_associative_impl<std::list<Complex>>(); }
void ranged_ctor_std_list_duplicates_strategy() { non_associative_container_duplicates_strategy<std::list>(); }
- void ranged_ctor_std_forward_list_int() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
- ranged_ctor_non_associative_impl<std::forward_list<int>>();
-#else
- QSKIP("<forward_list> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_forward_list_Movable() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
- ranged_ctor_non_associative_impl<std::forward_list<Movable>>();
-#else
- QSKIP("<forward_list> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_forward_list_Complex() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
- ranged_ctor_non_associative_impl<std::forward_list<Complex>>();
-#else
- QSKIP("<forward_list> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_forward_list_duplicates_strategy() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
- non_associative_container_duplicates_strategy<std::forward_list>();
-#else
- QSKIP("<forward_list> is needed for this test");
-#endif
- }
+ void ranged_ctor_std_forward_list_int() { ranged_ctor_non_associative_impl<std::forward_list<int>>(); }
+ void ranged_ctor_std_forward_list_Movable() {ranged_ctor_non_associative_impl<std::forward_list<Movable>>(); }
+ void ranged_ctor_std_forward_list_Complex() { ranged_ctor_non_associative_impl<std::forward_list<Complex>>(); }
+ void ranged_ctor_std_forward_list_duplicates_strategy() { non_associative_container_duplicates_strategy<std::forward_list>(); }
void ranged_ctor_std_set_int() { ranged_ctor_non_associative_impl<std::set<int>>(); }
void ranged_ctor_std_set_Movable() { ranged_ctor_non_associative_impl<std::set<Movable>>(); }
@@ -327,70 +276,15 @@ private Q_SLOTS:
void ranged_ctor_std_multiset_Complex() { ranged_ctor_non_associative_impl<std::multiset<Complex>>(); }
void ranged_ctor_std_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy<std::multiset>(); }
- void ranged_ctor_std_unordered_set_int() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- ranged_ctor_non_associative_impl<std::unordered_set<int>>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_unordered_set_Movable() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- ranged_ctor_non_associative_impl<std::unordered_set<Movable>>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_unordered_set_Complex() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- ranged_ctor_non_associative_impl<std::unordered_set<Complex>>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
-
- void ranged_ctor_unordered_set_duplicates_strategy() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- non_associative_container_duplicates_strategy<std::unordered_set>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
-
-
- void ranged_ctor_std_unordered_multiset_int() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- ranged_ctor_non_associative_impl<std::unordered_multiset<int>>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_unordered_multiset_Movable() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- ranged_ctor_non_associative_impl<std::unordered_multiset<Movable>>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
+ void ranged_ctor_std_unordered_set_int() { ranged_ctor_non_associative_impl<std::unordered_set<int>>(); }
+ void ranged_ctor_std_unordered_set_Movable() { ranged_ctor_non_associative_impl<std::unordered_set<Movable>>(); }
+ void ranged_ctor_std_unordered_set_Complex() { ranged_ctor_non_associative_impl<std::unordered_set<Complex>>(); }
+ void ranged_ctor_std_unordered_set_duplicates_strategy() { non_associative_container_duplicates_strategy<std::unordered_set>(); }
- void ranged_ctor_std_unordered_multiset_Complex() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- ranged_ctor_non_associative_impl<std::unordered_multiset<Complex>>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
-
- void ranged_ctor_std_unordered_multiset_duplicates_strategy() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
- non_associative_container_duplicates_strategy<std::unordered_multiset>();
-#else
- QSKIP("<unordered_set> is needed for this test");
-#endif
- }
+ void ranged_ctor_std_unordered_multiset_int() { ranged_ctor_non_associative_impl<std::unordered_multiset<int>>(); }
+ void ranged_ctor_std_unordered_multiset_Movable() { ranged_ctor_non_associative_impl<std::unordered_multiset<Movable>>(); }
+ void ranged_ctor_std_unordered_multiset_Complex() { ranged_ctor_non_associative_impl<std::unordered_multiset<Complex>>(); }
+ void ranged_ctor_std_unordered_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy<std::unordered_multiset>(); }
void ranged_ctor_QSet_int() { ranged_ctor_non_associative_impl<QSet<int>>(); }
void ranged_ctor_QSet_Movable() { ranged_ctor_non_associative_impl<QSet<Movable>>(); }
@@ -406,57 +300,17 @@ private Q_SLOTS:
void ranged_ctor_std_multimap_Movable() { ranged_ctor_associative_impl<std::multimap<Movable, int>>(); }
void ranged_ctor_std_multimap_Complex() { ranged_ctor_associative_impl<std::multimap<Complex, int>>(); }
- void ranged_ctor_unordered_map_int() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
- ranged_ctor_associative_impl<std::unordered_map<int, int>>();
-#else
- QSKIP("<unordered_map> is needed for this test");
-#endif
- }
-
- void ranged_ctor_unordered_map_Movable() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
- ranged_ctor_associative_impl<std::unordered_map<Movable, Movable>>();
-#else
- QSKIP("<unordered_map> is needed for this test");
-#endif
- }
-
- void ranged_ctor_unordered_map_Complex() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
- ranged_ctor_associative_impl<std::unordered_map<Complex, Complex>>();
-#else
- QSKIP("<unordered_map> is needed for this test");
-#endif
- }
+ void ranged_ctor_unordered_map_int() { ranged_ctor_associative_impl<std::unordered_map<int, int>>(); }
+ void ranged_ctor_unordered_map_Movable() { ranged_ctor_associative_impl<std::unordered_map<Movable, Movable>>(); }
+ void ranged_ctor_unordered_map_Complex() { ranged_ctor_associative_impl<std::unordered_map<Complex, Complex>>(); }
void ranged_ctor_QHash_int() { ranged_ctor_associative_impl<QHash<int, int>>(); }
void ranged_ctor_QHash_Movable() { ranged_ctor_associative_impl<QHash<Movable, int>>(); }
void ranged_ctor_QHash_Complex() { ranged_ctor_associative_impl<QHash<Complex, int>>(); }
- void ranged_ctor_unordered_multimap_int() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
- ranged_ctor_associative_impl<std::unordered_multimap<int, int>>();
-#else
- QSKIP("<unordered_map> is needed for this test");
-#endif
- }
-
- void ranged_ctor_unordered_multimap_Movable() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
- ranged_ctor_associative_impl<std::unordered_multimap<Movable, Movable>>();
-#else
- QSKIP("<unordered_map> is needed for this test");
-#endif
- }
-
- void ranged_ctor_unordered_multimap_Complex() {
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
- ranged_ctor_associative_impl<std::unordered_multimap<Complex, Complex>>();
-#else
- QSKIP("<unordered_map> is needed for this test");
-#endif
- }
+ void ranged_ctor_unordered_multimap_int() { ranged_ctor_associative_impl<std::unordered_multimap<int, int>>(); }
+ void ranged_ctor_unordered_multimap_Movable() { ranged_ctor_associative_impl<std::unordered_multimap<Movable, Movable>>(); }
+ void ranged_ctor_unordered_multimap_Complex() { ranged_ctor_associative_impl<std::unordered_multimap<Complex, Complex>>(); }
void ranged_ctor_QMultiHash_int() { ranged_ctor_associative_impl<QMultiHash<int, int>>(); }
void ranged_ctor_QMultiHash_Movable() { ranged_ctor_associative_impl<QMultiHash<Movable, int>>(); }
@@ -464,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:
@@ -471,10 +368,67 @@ private Q_SLOTS:
void front_back_QList() { front_back_impl<QList<qintptr>>(); }
void front_back_QVarLengthArray() { front_back_impl<QVarLengthArray<int>>(); }
void front_back_QString() { front_back_impl<QString>(); }
- void front_back_QStringRef() { front_back_impl<QStringRef>(); }
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()
@@ -533,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);
}
@@ -568,10 +535,8 @@ struct ContainerDuplicatedValuesStrategy<VarLengthArray<T...>> : ContainerAccept
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::list<T...>> : ContainerAcceptsDuplicateValues {};
-#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::forward_list<T...>> : ContainerAcceptsDuplicateValues {};
-#endif
// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution
template<typename ... T>
@@ -580,14 +545,12 @@ struct ContainerDuplicatedValuesStrategy<std::set<T...>> : ContainerRejectsDupli
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::multiset<T...>> : ContainerAcceptsDuplicateValues {};
-#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::unordered_set<T...>> : ContainerRejectsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::unordered_multiset<T...>> : ContainerAcceptsDuplicateValues {};
-#endif
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<QSet<T...>> : ContainerRejectsDuplicateValues {};
@@ -765,36 +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 <> QStringRef make(int size) { return s_string.leftRef(size); }
+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)