1/****************************************************************************
2**
3** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include "qbytearray.h"
32#include "qlinkedlist.h"
33#include "qlist.h"
34#include "qstring.h"
35#include "qvarlengtharray.h"
36#include "qvector.h"
37#include "qhash.h"
38#include "qdebug.h"
39
40#include <algorithm>
41#include <functional>
42#include <vector> // for reference
43#include <iostream>
44#include <list>
45#include <set>
46#include <sstream>
47#include <map>
48
49// MSVC has these containers from the Standard Library, but it lacks
50// a __has_include mechanism (that we need to use for other stdlibs).
51// For the sake of increasing our test coverage, work around the issue.
52
53#ifdef Q_CC_MSVC
54#define COMPILER_HAS_STDLIB_INCLUDE(x) 1
55#else
56#define COMPILER_HAS_STDLIB_INCLUDE(x) __has_include(x)
57#endif
58
59#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
60#include <forward_list>
61#endif
62#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
63#include <unordered_set>
64#endif
65#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
66#include <unordered_map>
67#endif
68
69QT_BEGIN_NAMESPACE
70std::ostream &operator<<(std::ostream &os, const QChar &c)
71{
72 Q_ASSERT(c == QLatin1Char{c.toLatin1()});
73 return os << c.toLatin1();
74}
75std::istream &operator>>(std::istream &os, QChar &c)
76{
77 char cL1;
78 os >> cL1;
79 c = QLatin1Char{cL1};
80 return os;
81}
82QT_END_NAMESPACE
83
84namespace {
85template <typename T>
86struct is_qlist : std::false_type {};
87template <typename T>
88struct is_qlist<QList<T>> : std::true_type {};
89}
90
91struct Movable
92{
93 explicit Movable(int i = 0) Q_DECL_NOTHROW
94 : i(i)
95 {
96 ++instanceCount;
97 }
98
99 Movable(const Movable &m)
100 : i(m.i)
101 {
102 ++instanceCount;
103 }
104
105 ~Movable()
106 {
107 --instanceCount;
108 }
109
110 int i;
111 static int instanceCount;
112
113 friend std::ostream &operator<<(std::ostream &os, const Movable &m)
114 { return os << m.i; }
115 friend std::istream &operator>>(std::istream &os, Movable &m)
116 { return os >> m.i; }
117};
118
119int Movable::instanceCount = 0;
120bool operator==(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i == rhs.i; }
121bool operator!=(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i != rhs.i; }
122bool operator<(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i < rhs.i; }
123
124uint qHash(Movable m, uint seed = 0) Q_DECL_NOTHROW { return qHash(key: m.i, seed); }
125QDebug &operator<<(QDebug &d, Movable m)
126{
127 const QDebugStateSaver saver(d);
128 return d.nospace() << "Movable(" << m.i << ")";
129}
130
131QT_BEGIN_NAMESPACE
132Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE);
133QT_END_NAMESPACE
134
135struct Complex
136{
137 explicit Complex(int i = 0) Q_DECL_NOTHROW
138 : i(i)
139 {
140 ++instanceCount;
141 }
142
143 Complex(const Complex &c)
144 : i(c.i)
145 {
146 ++instanceCount;
147 }
148
149 ~Complex()
150 {
151 --instanceCount;
152 }
153
154 int i;
155 static int instanceCount;
156
157 friend std::ostream &operator<<(std::ostream &os, const Complex &c)
158 { return os << c.i; }
159 friend std::istream &operator>>(std::istream &os, Complex &c)
160 { return os >> c.i; }
161};
162
163int Complex::instanceCount = 0;
164bool operator==(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i == rhs.i; }
165bool operator!=(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i != rhs.i; }
166bool operator<(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i < rhs.i; }
167
168uint qHash(Complex c, uint seed = 0) Q_DECL_NOTHROW { return qHash(key: c.i, seed); }
169QDebug &operator<<(QDebug &d, Complex c)
170{
171 const QDebugStateSaver saver(d);
172 return d.nospace() << "Complex(" << c.i << ")";
173}
174
175
176struct DuplicateStrategyTestType
177{
178 explicit DuplicateStrategyTestType(int i = 0) Q_DECL_NOTHROW
179 : i(i),
180 j(++counter)
181 {
182 }
183
184 int i;
185 int j;
186
187 static int counter;
188};
189
190int DuplicateStrategyTestType::counter = 0;
191
192// only look at the i member, not j. j allows us to identify which instance
193// gets inserted in containers that don't allow for duplicates
194bool operator==(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW
195{
196 return lhs.i == rhs.i;
197}
198
199bool operator!=(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW
200{
201 return lhs.i != rhs.i;
202}
203
204bool operator<(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW
205{
206 return lhs.i < rhs.i;
207}
208
209uint qHash(DuplicateStrategyTestType c, uint seed = 0) Q_DECL_NOTHROW
210{
211 return qHash(key: c.i, seed);
212}
213
214bool reallyEqual(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW
215{
216 return lhs.i == rhs.i && lhs.j == rhs.j;
217}
218
219QDebug &operator<<(QDebug &d, DuplicateStrategyTestType c)
220{
221 const QDebugStateSaver saver(d);
222 return d.nospace() << "DuplicateStrategyTestType(" << c.i << "," << c.j << ")";
223}
224
225
226namespace std {
227template<>
228struct hash<Movable>
229{
230 std::size_t operator()(Movable m) const Q_DECL_NOTHROW
231 {
232 return hash<int>()(m.i);
233 }
234};
235
236template<>
237struct hash<Complex>
238{
239 std::size_t operator()(Complex m) const Q_DECL_NOTHROW
240 {
241 return hash<int>()(m.i);
242 }
243};
244
245template<>
246struct hash<DuplicateStrategyTestType>
247{
248 std::size_t operator()(DuplicateStrategyTestType m) const Q_DECL_NOTHROW
249 {
250 return hash<int>()(m.i);
251 }
252};
253}
254
255// work around the fact that QVarLengthArray has a non-type
256// template parameter, and that breaks non_associative_container_duplicates_strategy
257template<typename T>
258class VarLengthArray : public QVarLengthArray<T>
259{
260public:
261#ifdef Q_COMPILER_INHERITING_CONSTRUCTORS
262 using QVarLengthArray<T>::QVarLengthArray;
263#else
264 template<typename InputIterator>
265 VarLengthArray(InputIterator first, InputIterator last)
266 : QVarLengthArray<T>(first, last)
267 {
268 }
269
270 VarLengthArray(std::initializer_list<T> args)
271 : QVarLengthArray<T>(args)
272 {
273 }
274#endif
275};
276
277class tst_ContainerApiSymmetry : public QObject
278{
279 Q_OBJECT
280
281 int m_movableInstanceCount;
282 int m_complexInstanceCount;
283
284private Q_SLOTS:
285 void init();
286 void cleanup();
287
288private:
289 template <typename Container>
290 void ranged_ctor_non_associative_impl() const;
291
292 template<template<typename ... T> class Container>
293 void non_associative_container_duplicates_strategy() const;
294
295 template <typename Container>
296 void ranged_ctor_associative_impl() const;
297
298private Q_SLOTS:
299 // non associative
300 void ranged_ctor_std_vector_int() { ranged_ctor_non_associative_impl<std::vector<int>>(); }
301 void ranged_ctor_std_vector_char() { ranged_ctor_non_associative_impl<std::vector<char>>(); }
302 void ranged_ctor_std_vector_QChar() { ranged_ctor_non_associative_impl<std::vector<QChar>>(); }
303 void ranged_ctor_std_vector_Movable() { ranged_ctor_non_associative_impl<std::vector<Movable>>(); }
304 void ranged_ctor_std_vector_Complex() { ranged_ctor_non_associative_impl<std::vector<Complex>>(); }
305 void ranged_ctor_std_vector_duplicates_strategy() { non_associative_container_duplicates_strategy<std::vector>(); }
306
307 void ranged_ctor_QVector_int() { ranged_ctor_non_associative_impl<QVector<int>>(); }
308 void ranged_ctor_QVector_char() { ranged_ctor_non_associative_impl<QVector<char>>(); }
309 void ranged_ctor_QVector_QChar() { ranged_ctor_non_associative_impl<QVector<QChar>>(); }
310 void ranged_ctor_QVector_Movable() { ranged_ctor_non_associative_impl<QVector<Movable>>(); }
311 void ranged_ctor_QVector_Complex() { ranged_ctor_non_associative_impl<QVector<Complex>>(); }
312 void ranged_ctor_QVector_duplicates_strategy() { non_associative_container_duplicates_strategy<QVector>(); }
313
314 void ranged_ctor_QVarLengthArray_int() { ranged_ctor_non_associative_impl<QVarLengthArray<int>>(); }
315 void ranged_ctor_QVarLengthArray_Movable() { ranged_ctor_non_associative_impl<QVarLengthArray<Movable>>(); }
316 void ranged_ctor_QVarLengthArray_Complex() { ranged_ctor_non_associative_impl<QVarLengthArray<Complex>>(); }
317 void ranged_ctor_QVarLengthArray_duplicates_strategy() { non_associative_container_duplicates_strategy<VarLengthArray>(); } // note the VarLengthArray passed
318
319 void ranged_ctor_QList_int() { ranged_ctor_non_associative_impl<QList<int>>(); }
320 void ranged_ctor_QList_Movable() { ranged_ctor_non_associative_impl<QList<Movable>>(); }
321 void ranged_ctor_QList_Complex() { ranged_ctor_non_associative_impl<QList<Complex>>(); }
322 void ranged_ctor_QList_duplicates_strategy() { non_associative_container_duplicates_strategy<QList>(); }
323
324 void ranged_ctor_std_list_int() { ranged_ctor_non_associative_impl<std::list<int>>(); }
325 void ranged_ctor_std_list_Movable() { ranged_ctor_non_associative_impl<std::list<Movable>>(); }
326 void ranged_ctor_std_list_Complex() { ranged_ctor_non_associative_impl<std::list<Complex>>(); }
327 void ranged_ctor_std_list_duplicates_strategy() { non_associative_container_duplicates_strategy<std::list>(); }
328
329 void ranged_ctor_std_forward_list_int() {
330#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
331 ranged_ctor_non_associative_impl<std::forward_list<int>>();
332#else
333 QSKIP("<forward_list> is needed for this test");
334#endif
335 }
336
337 void ranged_ctor_std_forward_list_Movable() {
338#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
339 ranged_ctor_non_associative_impl<std::forward_list<Movable>>();
340#else
341 QSKIP("<forward_list> is needed for this test");
342#endif
343 }
344
345 void ranged_ctor_std_forward_list_Complex() {
346#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
347 ranged_ctor_non_associative_impl<std::forward_list<Complex>>();
348#else
349 QSKIP("<forward_list> is needed for this test");
350#endif
351 }
352
353 void ranged_ctor_std_forward_list_duplicates_strategy() {
354#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
355 non_associative_container_duplicates_strategy<std::forward_list>();
356#else
357 QSKIP("<forward_list> is needed for this test");
358#endif
359 }
360
361#if QT_DEPRECATED_SINCE(5, 15)
362 void ranged_ctor_QLinkedList_int();
363 void ranged_ctor_QLinkedList_Movable();
364 void ranged_ctor_QLinkedList_Complex();
365 void ranged_ctor_QLinkedList_duplicates_strategy();
366#endif
367 void ranged_ctor_std_set_int() { ranged_ctor_non_associative_impl<std::set<int>>(); }
368 void ranged_ctor_std_set_Movable() { ranged_ctor_non_associative_impl<std::set<Movable>>(); }
369 void ranged_ctor_std_set_Complex() { ranged_ctor_non_associative_impl<std::set<Complex>>(); }
370 void ranged_ctor_std_set_duplicates_strategy() { non_associative_container_duplicates_strategy<std::set>(); }
371
372 void ranged_ctor_std_multiset_int() { ranged_ctor_non_associative_impl<std::multiset<int>>(); }
373 void ranged_ctor_std_multiset_Movable() { ranged_ctor_non_associative_impl<std::multiset<Movable>>(); }
374 void ranged_ctor_std_multiset_Complex() { ranged_ctor_non_associative_impl<std::multiset<Complex>>(); }
375 void ranged_ctor_std_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy<std::multiset>(); }
376
377 void ranged_ctor_std_unordered_set_int() {
378#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
379 ranged_ctor_non_associative_impl<std::unordered_set<int>>();
380#else
381 QSKIP("<unordered_set> is needed for this test");
382#endif
383 }
384
385 void ranged_ctor_std_unordered_set_Movable() {
386#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
387 ranged_ctor_non_associative_impl<std::unordered_set<Movable>>();
388#else
389 QSKIP("<unordered_set> is needed for this test");
390#endif
391 }
392
393 void ranged_ctor_std_unordered_set_Complex() {
394#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
395 ranged_ctor_non_associative_impl<std::unordered_set<Complex>>();
396#else
397 QSKIP("<unordered_set> is needed for this test");
398#endif
399 }
400
401 void ranged_ctor_unordered_set_duplicates_strategy() {
402#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
403 non_associative_container_duplicates_strategy<std::unordered_set>();
404#else
405 QSKIP("<unordered_set> is needed for this test");
406#endif
407 }
408
409
410 void ranged_ctor_std_unordered_multiset_int() {
411#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
412 ranged_ctor_non_associative_impl<std::unordered_multiset<int>>();
413#else
414 QSKIP("<unordered_set> is needed for this test");
415#endif
416 }
417
418 void ranged_ctor_std_unordered_multiset_Movable() {
419#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
420 ranged_ctor_non_associative_impl<std::unordered_multiset<Movable>>();
421#else
422 QSKIP("<unordered_set> is needed for this test");
423#endif
424 }
425
426 void ranged_ctor_std_unordered_multiset_Complex() {
427#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
428 ranged_ctor_non_associative_impl<std::unordered_multiset<Complex>>();
429#else
430 QSKIP("<unordered_set> is needed for this test");
431#endif
432 }
433
434 void ranged_ctor_std_unordered_multiset_duplicates_strategy() {
435#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
436 non_associative_container_duplicates_strategy<std::unordered_multiset>();
437#else
438 QSKIP("<unordered_set> is needed for this test");
439#endif
440 }
441
442 void ranged_ctor_QSet_int() { ranged_ctor_non_associative_impl<QSet<int>>(); }
443 void ranged_ctor_QSet_Movable() { ranged_ctor_non_associative_impl<QSet<Movable>>(); }
444 void ranged_ctor_QSet_Complex() { ranged_ctor_non_associative_impl<QSet<Complex>>(); }
445 void ranged_ctor_QSet_duplicates_strategy() { non_associative_container_duplicates_strategy<QSet>(); }
446
447 // associative
448 void ranged_ctor_std_map_int() { ranged_ctor_associative_impl<std::map<int, int>>(); }
449 void ranged_ctor_std_map_Movable() { ranged_ctor_associative_impl<std::map<Movable, int>>(); }
450 void ranged_ctor_std_map_Complex() { ranged_ctor_associative_impl<std::map<Complex, int>>(); }
451
452 void ranged_ctor_std_multimap_int() { ranged_ctor_associative_impl<std::multimap<int, int>>(); }
453 void ranged_ctor_std_multimap_Movable() { ranged_ctor_associative_impl<std::multimap<Movable, int>>(); }
454 void ranged_ctor_std_multimap_Complex() { ranged_ctor_associative_impl<std::multimap<Complex, int>>(); }
455
456 void ranged_ctor_unordered_map_int() {
457#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
458 ranged_ctor_associative_impl<std::unordered_map<int, int>>();
459#else
460 QSKIP("<unordered_map> is needed for this test");
461#endif
462 }
463
464 void ranged_ctor_unordered_map_Movable() {
465#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
466 ranged_ctor_associative_impl<std::unordered_map<Movable, Movable>>();
467#else
468 QSKIP("<unordered_map> is needed for this test");
469#endif
470 }
471
472 void ranged_ctor_unordered_map_Complex() {
473#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
474 ranged_ctor_associative_impl<std::unordered_map<Complex, Complex>>();
475#else
476 QSKIP("<unordered_map> is needed for this test");
477#endif
478 }
479
480 void ranged_ctor_QHash_int() { ranged_ctor_associative_impl<QHash<int, int>>(); }
481 void ranged_ctor_QHash_Movable() { ranged_ctor_associative_impl<QHash<Movable, int>>(); }
482 void ranged_ctor_QHash_Complex() { ranged_ctor_associative_impl<QHash<Complex, int>>(); }
483
484 void ranged_ctor_unordered_multimap_int() {
485#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
486 ranged_ctor_associative_impl<std::unordered_multimap<int, int>>();
487#else
488 QSKIP("<unordered_map> is needed for this test");
489#endif
490 }
491
492 void ranged_ctor_unordered_multimap_Movable() {
493#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
494 ranged_ctor_associative_impl<std::unordered_multimap<Movable, Movable>>();
495#else
496 QSKIP("<unordered_map> is needed for this test");
497#endif
498 }
499
500 void ranged_ctor_unordered_multimap_Complex() {
501#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_map>)
502 ranged_ctor_associative_impl<std::unordered_multimap<Complex, Complex>>();
503#else
504 QSKIP("<unordered_map> is needed for this test");
505#endif
506 }
507
508 void ranged_ctor_QMultiHash_int() { ranged_ctor_associative_impl<QMultiHash<int, int>>(); }
509 void ranged_ctor_QMultiHash_Movable() { ranged_ctor_associative_impl<QMultiHash<Movable, int>>(); }
510 void ranged_ctor_QMultiHash_Complex() { ranged_ctor_associative_impl<QMultiHash<Complex, int>>(); }
511
512private:
513 template <typename Container>
514 void front_back_impl() const;
515
516private Q_SLOTS:
517 void front_back_std_vector() { front_back_impl<std::vector<int>>(); }
518 void front_back_QVector() { front_back_impl<QVector<int>>(); }
519 void front_back_QList() { front_back_impl<QList<qintptr>>(); }
520#if QT_DEPRECATED_SINCE(5, 15)
521 void front_back_QLinkedList();
522#endif
523 void front_back_QVarLengthArray() { front_back_impl<QVarLengthArray<int>>(); }
524 void front_back_QString() { front_back_impl<QString>(); }
525 void front_back_QStringRef() { front_back_impl<QStringRef>(); }
526 void front_back_QStringView() { front_back_impl<QStringView>(); }
527 void front_back_QLatin1String() { front_back_impl<QLatin1String>(); }
528 void front_back_QByteArray() { front_back_impl<QByteArray>(); }
529};
530
531void tst_ContainerApiSymmetry::init()
532{
533 m_movableInstanceCount = Movable::instanceCount;
534 m_complexInstanceCount = Complex::instanceCount;
535}
536
537void tst_ContainerApiSymmetry::cleanup()
538{
539 // very simple leak check
540 QCOMPARE(Movable::instanceCount, m_movableInstanceCount);
541 QCOMPARE(Complex::instanceCount, m_complexInstanceCount);
542}
543
544template <typename Container>
545Container createContainerReference()
546{
547 using V = typename Container::value_type;
548
549 return {V(0), V(1), V(2), V(0)};
550}
551
552template <typename Container>
553void tst_ContainerApiSymmetry::ranged_ctor_non_associative_impl() const
554{
555 using V = typename Container::value_type;
556
557 // the double V(0) is deliberate
558 const auto reference = createContainerReference<Container>();
559
560 // plain array
561 const V values1[] = { V(0), V(1), V(2), V(0) };
562
563 const Container c1(values1, values1 + sizeof(values1)/sizeof(values1[0]));
564
565 // from QList
566 QList<V> l2;
567 l2 << V(0) << V(1) << V(2) << V(0);
568
569 const Container c2a(l2.begin(), l2.end());
570 const Container c2b(l2.cbegin(), l2.cend());
571
572 // from std::list
573 std::list<V> l3;
574 l3.push_back(V(0));
575 l3.push_back(V(1));
576 l3.push_back(V(2));
577 l3.push_back(V(0));
578 const Container c3a(l3.begin(), l3.end());
579
580 // from const std::list
581 const std::list<V> l3c = l3;
582 const Container c3b(l3c.begin(), l3c.end());
583
584 // from itself
585 const Container c4(reference.begin(), reference.end());
586
587 // from stringsteam (= pure input_iterator)
588 const Container c5 = [&] {
589#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // QTBUG-99036
590 if constexpr (is_qlist<Container>::value) {
591 return c4;
592 } else
593#endif
594 {
595 std::stringstream ss;
596 for (auto &v : values1)
597 ss << v << ' ';
598 ss.seekg(0);
599 return Container(std::istream_iterator<V>{ss},
600 std::istream_iterator<V>{});
601 }
602 }();
603
604 QCOMPARE(c1, reference);
605 QCOMPARE(c2a, reference);
606 QCOMPARE(c2b, reference);
607 QCOMPARE(c3a, reference);
608 QCOMPARE(c3b, reference);
609 QCOMPARE(c4, reference);
610 QCOMPARE(c5, reference);
611}
612
613
614// type traits for detecting whether a non-associative container
615// accepts duplicated values, and if it doesn't, whether construction/insertion
616// prefer the new values (overwriting) or the old values (rejecting)
617
618struct ContainerAcceptsDuplicateValues {};
619struct ContainerOverwritesDuplicateValues {};
620struct ContainerRejectsDuplicateValues {};
621
622template<typename Container>
623struct ContainerDuplicatedValuesStrategy {};
624
625template<typename ... T>
626struct ContainerDuplicatedValuesStrategy<std::vector<T...>> : ContainerAcceptsDuplicateValues {};
627
628template<typename ... T>
629struct ContainerDuplicatedValuesStrategy<QVector<T...>> : ContainerAcceptsDuplicateValues {};
630
631template<typename ... T>
632struct ContainerDuplicatedValuesStrategy<QVarLengthArray<T...>> : ContainerAcceptsDuplicateValues {};
633
634template<typename ... T>
635struct ContainerDuplicatedValuesStrategy<VarLengthArray<T...>> : ContainerAcceptsDuplicateValues {};
636
637template<typename ... T>
638struct ContainerDuplicatedValuesStrategy<QList<T...>> : ContainerAcceptsDuplicateValues {};
639
640template<typename ... T>
641struct ContainerDuplicatedValuesStrategy<std::list<T...>> : ContainerAcceptsDuplicateValues {};
642
643#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
644template<typename ... T>
645struct ContainerDuplicatedValuesStrategy<std::forward_list<T...>> : ContainerAcceptsDuplicateValues {};
646#endif
647
648#if QT_DEPRECATED_SINCE(5, 15)
649QT_WARNING_PUSH
650QT_WARNING_DISABLE_DEPRECATED
651template<typename ... T>
652struct ContainerDuplicatedValuesStrategy<QLinkedList<T...>> : ContainerAcceptsDuplicateValues {};
653QT_WARNING_POP
654#endif
655
656// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution
657template<typename ... T>
658struct ContainerDuplicatedValuesStrategy<std::set<T...>> : ContainerRejectsDuplicateValues {};
659
660template<typename ... T>
661struct ContainerDuplicatedValuesStrategy<std::multiset<T...>> : ContainerAcceptsDuplicateValues {};
662
663#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
664// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution
665template<typename ... T>
666struct ContainerDuplicatedValuesStrategy<std::unordered_set<T...>> : ContainerRejectsDuplicateValues {};
667
668template<typename ... T>
669struct ContainerDuplicatedValuesStrategy<std::unordered_multiset<T...>> : ContainerAcceptsDuplicateValues {};
670#endif
671
672template<typename ... T>
673struct ContainerDuplicatedValuesStrategy<QSet<T...>> : ContainerRejectsDuplicateValues {};
674
675template<typename Container>
676void non_associative_container_check_duplicates_impl(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, ContainerAcceptsDuplicateValues)
677{
678 // do a deep check for equality, not ordering
679 QVERIFY(std::distance(reference.begin(), reference.end()) == std::distance(c.begin(), c.end()));
680 QVERIFY(std::is_permutation(reference.begin(), reference.end(), c.begin(), &reallyEqual));
681}
682
683enum class IterationOnReference
684{
685 ForwardIteration,
686 ReverseIteration
687};
688
689template<typename Container>
690void non_associative_container_check_duplicates_impl_no_duplicates(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, IterationOnReference ior)
691{
692 std::vector<DuplicateStrategyTestType> valuesAlreadySeen;
693
694 // iterate on reference forward or backwards, depending on ior. this will give
695 // us the expected semantics when checking for duplicated values into c
696 auto it = [&reference, ior]() {
697 switch (ior) {
698 case IterationOnReference::ForwardIteration: return reference.begin();
699 case IterationOnReference::ReverseIteration: return reference.end() - 1;
700 };
701 return std::initializer_list<DuplicateStrategyTestType>::const_iterator();
702 }();
703
704 const auto &end = [&reference, ior]() {
705 switch (ior) {
706 case IterationOnReference::ForwardIteration: return reference.end();
707 case IterationOnReference::ReverseIteration: return reference.begin() - 1;
708 };
709 return std::initializer_list<DuplicateStrategyTestType>::const_iterator();
710 }();
711
712 while (it != end) {
713 const auto &value = *it;
714
715 // check that there is indeed the same value in the container (using operator==)
716 const auto &valueInContainerIterator = std::find(c.begin(), c.end(), value);
717 QVERIFY(valueInContainerIterator != c.end());
718 QVERIFY(value == *valueInContainerIterator);
719
720 // if the value is a duplicate, we don't expect to find it in the container
721 // (when doing a deep comparison). otherwise it should be there
722
723 const auto &valuesAlreadySeenIterator = std::find(valuesAlreadySeen.cbegin(), valuesAlreadySeen.cend(), value);
724 const bool valueIsDuplicated = (valuesAlreadySeenIterator != valuesAlreadySeen.cend());
725
726 const auto &reallyEqualCheck = [&value](const DuplicateStrategyTestType &v) { return reallyEqual(value, v); };
727 QCOMPARE(std::find_if(c.begin(), c.end(), reallyEqualCheck) == c.end(), valueIsDuplicated);
728
729 valuesAlreadySeen.push_back(value);
730
731 switch (ior) {
732 case IterationOnReference::ForwardIteration:
733 ++it;
734 break;
735 case IterationOnReference::ReverseIteration:
736 --it;
737 break;
738 };
739 }
740
741}
742
743template<typename Container>
744void non_associative_container_check_duplicates_impl(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, ContainerRejectsDuplicateValues)
745{
746 non_associative_container_check_duplicates_impl_no_duplicates(reference, c, IterationOnReference::ForwardIteration);
747}
748
749template<typename Container>
750void non_associative_container_check_duplicates_impl(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, ContainerOverwritesDuplicateValues)
751{
752 non_associative_container_check_duplicates_impl_no_duplicates(reference, c, IterationOnReference::ReverseIteration);
753}
754
755template<typename Container>
756void non_associative_container_check_duplicates(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c)
757{
758 non_associative_container_check_duplicates_impl(reference, c, ContainerDuplicatedValuesStrategy<Container>());
759}
760
761template<template<class ... T> class Container>
762void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() const
763{
764 // first and last are "duplicates" -- they compare equal for operator==,
765 // but they differ when using reallyEqual
766 const std::initializer_list<DuplicateStrategyTestType> reference{ DuplicateStrategyTestType{0},
767 DuplicateStrategyTestType{1},
768 DuplicateStrategyTestType{2},
769 DuplicateStrategyTestType{0} };
770 Container<DuplicateStrategyTestType> c1{reference};
771 non_associative_container_check_duplicates(reference, c1);
772
773 Container<DuplicateStrategyTestType> c2{reference.begin(), reference.end()};
774 non_associative_container_check_duplicates(reference, c2);
775}
776
777template <typename Container>
778void tst_ContainerApiSymmetry::ranged_ctor_associative_impl() const
779{
780 using K = typename Container::key_type;
781 using V = typename Container::mapped_type;
782
783 // The double K(0) is deliberate. The order of the elements matters:
784 // * for unique-key STL containers, the first one should be the one inserted (cf. LWG 2844)
785 // * for unique-key Qt containers, the last one should be the one inserted
786 // * for multi-key sorted containers, the order of insertion of identical keys is also the
787 // iteration order (which establishes the equality of the containers)
788 // (although nothing of this is being tested here, that deserves its own testing)
789 const Container reference{
790 { K(0), V(1000) },
791 { K(1), V(1001) },
792 { K(2), V(1002) },
793 { K(0), V(1003) }
794 };
795
796 // Note that using anything not convertible to std::pair doesn't work for
797 // std containers. Their ranged construction is defined in terms of
798 // insert(value_type), which for std associative containers is
799 // std::pair<const K, T>.
800
801 // plain array
802 const std::pair<K, V> values1[] = {
803 std::make_pair(K(0), V(1000)),
804 std::make_pair(K(1), V(1001)),
805 std::make_pair(K(2), V(1002)),
806 std::make_pair(K(0), V(1003))
807 };
808
809 const Container c1(values1, values1 + sizeof(values1)/sizeof(values1[0]));
810
811 // from QList
812 QList<std::pair<K, V>> l2;
813 l2 << std::make_pair(K(0), V(1000))
814 << std::make_pair(K(1), V(1001))
815 << std::make_pair(K(2), V(1002))
816 << std::make_pair(K(0), V(1003));
817
818 const Container c2a(l2.begin(), l2.end());
819 const Container c2b(l2.cbegin(), l2.cend());
820
821 // from std::list
822 std::list<std::pair<K, V>> l3;
823 l3.push_back(std::make_pair(K(0), V(1000)));
824 l3.push_back(std::make_pair(K(1), V(1001)));
825 l3.push_back(std::make_pair(K(2), V(1002)));
826 l3.push_back(std::make_pair(K(0), V(1003)));
827 const Container c3a(l3.begin(), l3.end());
828
829 // from const std::list
830 const std::list<std::pair<K, V>> l3c = l3;
831 const Container c3b(l3c.begin(), l3c.end());
832
833 // from itself
834 const Container c4(reference.begin(), reference.end());
835
836 QCOMPARE(c1, reference);
837 QCOMPARE(c2a, reference);
838 QCOMPARE(c2b, reference);
839 QCOMPARE(c3a, reference);
840 QCOMPARE(c3b, reference);
841 QCOMPARE(c4, reference);
842}
843
844template <typename Container>
845Container make(int size)
846{
847 Container c;
848 int i = 1;
849 while (size--)
850 c.push_back(typename Container::value_type(i++));
851 return c;
852}
853
854static QString s_string = QStringLiteral("\1\2\3\4\5\6\7");
855
856template <> QStringRef make(int size) { return s_string.leftRef(n: size); }
857template <> QStringView make(int size) { return QStringView(s_string).left(n: size); }
858template <> QLatin1String make(int size) { return QLatin1String("\1\2\3\4\5\6\7", size); }
859
860template <typename T> T clean(T &&t) { return std::forward<T>(t); }
861inline QChar clean(QCharRef ch) { return ch; }
862inline char clean(QByteRef ch) { return ch; }
863inline char clean(QLatin1Char ch) { return ch.toLatin1(); }
864
865template <typename Container>
866void tst_ContainerApiSymmetry::front_back_impl() const
867{
868 using V = typename Container::value_type;
869 auto c1 = make<Container>(1);
870 QCOMPARE(clean(c1.front()), V(1));
871 QCOMPARE(clean(c1.back()), V(1));
872 QCOMPARE(clean(qAsConst(c1).front()), V(1));
873 QCOMPARE(clean(qAsConst(c1).back()), V(1));
874
875 auto c2 = make<Container>(2);
876 QCOMPARE(clean(c2.front()), V(1));
877 QCOMPARE(clean(c2.back()), V(2));
878 QCOMPARE(clean(qAsConst(c2).front()), V(1));
879 QCOMPARE(clean(qAsConst(c2).back()), V(2));
880}
881
882#if QT_DEPRECATED_SINCE(5, 15)
883QT_WARNING_PUSH
884QT_WARNING_DISABLE_DEPRECATED
885void tst_ContainerApiSymmetry::ranged_ctor_QLinkedList_int()
886{
887 ranged_ctor_non_associative_impl<QLinkedList<int>>();
888}
889
890void tst_ContainerApiSymmetry::ranged_ctor_QLinkedList_Movable()
891{
892 ranged_ctor_non_associative_impl<QLinkedList<Movable>>();
893}
894
895void tst_ContainerApiSymmetry::ranged_ctor_QLinkedList_Complex()
896{
897 ranged_ctor_non_associative_impl<QLinkedList<Complex>>();
898}
899
900void tst_ContainerApiSymmetry::ranged_ctor_QLinkedList_duplicates_strategy()
901{
902 non_associative_container_duplicates_strategy<QLinkedList>();
903}
904
905void tst_ContainerApiSymmetry::front_back_QLinkedList()
906{
907 front_back_impl<QLinkedList<int>>();
908}
909QT_WARNING_POP
910#endif
911
912QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry)
913#include "tst_containerapisymmetry.moc"
914

source code of qtbase/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp