1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QCOMPARISONTESTHELPER_P_H
5#define QCOMPARISONTESTHELPER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/q20type_traits.h>
19#include <QtCore/qxptype_traits.h>
20#include <QtTest/qtest.h>
21
22QT_BEGIN_NAMESPACE
23
24namespace QTestPrivate {
25
26#ifdef __cpp_lib_three_way_comparison
27template <typename LT, typename RT>
28using HasThreeWayComparisonOp = decltype(std::declval<LT>() <=> std::declval<RT>());
29
30template <typename LT, typename RT>
31constexpr bool implementsThreeWayComparisonOp_v = qxp::is_detected_v<HasThreeWayComparisonOp,
32 LT, RT>;
33#endif
34
35Q_TESTLIB_EXPORT QByteArray formatTypeWithCRefImpl(QMetaType type, bool isConst,
36 bool isRef, bool isRvalueRef);
37
38template <typename T>
39QByteArray formatTypeWithCRef()
40{
41 return formatTypeWithCRefImpl(QMetaType::fromType<q20::remove_cvref_t<T>>(),
42 std::is_const_v<std::remove_reference_t<T>>,
43 std::is_reference_v<T>,
44 std::is_rvalue_reference_v<T>);
45}
46
47#define FOR_EACH_CREF(Func, Left, Right, Op, Result) \
48 Func(Left &, Right &, Op, Result) \
49 Func(Left &, Right const &, Op, Result) \
50 Func(Left &, Right &&, Op, Result) \
51 Func(Left &, Right const &&, Op, Result) \
52 Func(Left const &, Right &, Op, Result) \
53 Func(Left const &, Right const &, Op, Result) \
54 Func(Left const &, Right &&, Op, Result) \
55 Func(Left const &, Right const &&, Op, Result) \
56 Func(Left &&, Right &, Op, Result) \
57 Func(Left &&, Right const &, Op, Result) \
58 Func(Left &&, Right &&, Op, Result) \
59 Func(Left &&, Right const &&, Op, Result) \
60 Func(Left const &&, Right &, Op, Result) \
61 Func(Left const &&, Right const &, Op, Result) \
62 Func(Left const &&, Right &&, Op, Result) \
63 Func(Left const &&, Right const &&, Op, Result) \
64 /* END */
65
66#define CHECK_SINGLE_OPERATOR(Left, Right, Op, Result) \
67 do { \
68 static_assert(std::is_convertible_v<decltype( \
69 std::declval<Left>() Op std::declval<Right>()), Result>); \
70 if constexpr (!std::is_same_v<Left, Right>) { \
71 static_assert(std::is_convertible_v<decltype( \
72 std::declval<Right>() Op std::declval<Left>()), Result>); \
73 } \
74 } while (false); \
75 /* END */
76
77/*!
78 \internal
79
80 This function checks that the types \c LeftType and \c RightType properly
81 define {in}equality operators (== and !=). The checks are performed for
82 all combinations of cvref-qualified lvalues and rvalues.
83*/
84template <typename LeftType, typename RightType = LeftType>
85void testEqualityOperatorsCompile()
86{
87 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, ==, bool)
88 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, !=, bool)
89}
90
91/*!
92 \internal
93
94 This function checks that the types \c LeftType and \c RightType properly
95 define all comparison operators (==, !=, <, >, <=, >=). The checks are
96 performed for all combinations of cvref-qualified lvalues and rvalues.
97
98 If compiled in C++20 mode, also checks \c {operator<=>()} if that is
99 implemented.
100*/
101template <typename LeftType, typename RightType = LeftType>
102void testAllComparisonOperatorsCompile()
103{
104 testEqualityOperatorsCompile<LeftType, RightType>();
105 if (QTest::currentTestFailed())
106 return;
107 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, >, bool)
108 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, <, bool)
109 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, >=, bool)
110 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, <=, bool)
111#ifdef __cpp_lib_three_way_comparison
112 if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
113 FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, <=>, std::partial_ordering)
114 }
115#endif
116}
117
118#undef CHECK_SINGLE_OPERATOR
119#undef FOR_EACH_CREF
120
121#define CHECK_RUNTIME_CREF(Func, Left, Right, Op, Expected) \
122 do { \
123 Func(Left, Right, Op, Expected); \
124 Func(std::as_const(Left), Right, Op, Expected); \
125 Func(Left, std::as_const(Right), Op, Expected); \
126 Func(std::as_const(Left), std::as_const(Right), Op, Expected); \
127 } while (false) \
128 /* END */
129
130#define CHECK_RUNTIME_LR(Left, Right, Op, Expected) \
131 do { \
132 QCOMPARE_EQ(Left Op Right, Expected); \
133 QCOMPARE_EQ(std::move(Left) Op Right, Expected); \
134 QCOMPARE_EQ(Left Op std::move(Right), Expected); \
135 QCOMPARE_EQ(std::move(Left) Op std::move(Right), Expected); \
136 } while (false) \
137 /* END */
138
139#ifdef __cpp_lib_three_way_comparison
140
141// Hide the macro under an ifdef, because it otherwise triggers a warning
142// in Clang C++17 build.
143#define CHECK_RUNTIME_3WAY(Left, Right, Op, Expected) \
144 do { \
145 QCOMPARE_EQ((Left <=> Right) Op 0, Expected); \
146 QCOMPARE_EQ((std::move(Left) <=> Right) Op 0, Expected); \
147 QCOMPARE_EQ((Left <=> std::move(Right)) Op 0, Expected); \
148 QCOMPARE_EQ((std::move(Left) <=> std::move(Right)) Op 0, Expected); \
149 } while (false) \
150 /* END */
151
152#endif // __cpp_lib_three_way_comparison
153
154/*!
155 \internal
156 Basic testing of equality operators.
157
158 The helper function tests {in}equality operators (== and !=) for the \a lhs
159 operand of type \c {LeftType} and the \a rhs operand of type \c {RightType},
160 plus the reverse order of the operands.
161
162 The \a expectedEqual parameter is an expected result for \c {operator==()}.
163
164 \note Any test calling this method will need to check the test state after
165 doing so, if there is any later code in the test.
166
167 \code
168 QTime early(12, 34, 56, 00);
169 QTime later(12, 34, 56, 01);
170 QTestPrivate::testEqualityOperators(early, later, false);
171 if (QTest:currentTestFailed())
172 return;
173 \endcode
174*/
175template <typename LeftType, typename RightType>
176void testEqualityOperators(LeftType lhs, RightType rhs, bool expectedEqual)
177{
178 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==, expectedEqual);
179 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=, !expectedEqual);
180 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==, expectedEqual);
181 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=, !expectedEqual);
182}
183
184/*!
185 \internal
186 Basic testing of equality and relation operators.
187
188 The helper function tests all six relation and equality operators
189 (==, !=, <, >, <=, >=) for the \a lhs operand of type \c {LeftType} and
190 the \a rhs operand of type \c {RightType} and all six for the reverse
191 order of the operands.
192
193 If compiled in C++20 mode, also checks \c {operator<=>()} if that is
194 implemented.
195
196 When compiled in C++17 mode, the \c OrderingType must be one of
197 Qt::partial_ordering, Qt::strong_ordering, or Qt::weak_ordering.
198 In C++20 mode, also the \c {std::*_ordering} types can be used.
199
200 The \a expectedOrdering parameter provides the expected
201 relation between \a lhs and \a rhs.
202
203 \note Any test calling this method will need to check the test state after
204 doing so, if there is any later code in the test.
205
206 \code
207 QDateTime now = QDateTime::currentDateTime();
208 QDateTime later = now.addMSec(1);
209 QTestPrivate::testComparisonOperators(now, later, Qt::weak_ordering::less);
210 if (QTest:currentTestFailed())
211 return;
212 \endcode
213*/
214template <typename LeftType, typename RightType, typename OrderingType>
215void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expectedOrdering)
216{
217 constexpr bool isQOrderingType = std::is_same_v<OrderingType, Qt::partial_ordering>
218 || std::is_same_v<OrderingType, Qt::weak_ordering>
219 || std::is_same_v<OrderingType, Qt::strong_ordering>;
220#ifdef __cpp_lib_three_way_comparison
221 constexpr bool isStdOrderingType = std::is_same_v<OrderingType, std::partial_ordering>
222 || std::is_same_v<OrderingType, std::weak_ordering>
223 || std::is_same_v<OrderingType, std::strong_ordering>;
224#else
225 constexpr bool isStdOrderingType = false;
226#endif
227
228 static_assert(isQOrderingType || isStdOrderingType,
229 "Please provide, as the expectedOrdering parameter, a value "
230 "of one of the Qt::{partial,weak,strong}_ordering or "
231 "std::{partial,weak,strong}_ordering types.");
232
233 // We have all sorts of operator==() between Q*Ordering and std::*_ordering
234 // types, so we can just compare to Qt::partial_ordering.
235 const bool expectedEqual = expectedOrdering == Qt::partial_ordering::equivalent;
236 const bool expectedLess = expectedOrdering == Qt::partial_ordering::less;
237 const bool expectedUnordered = expectedOrdering == Qt::partial_ordering::unordered;
238
239 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==,
240 !expectedUnordered && expectedEqual);
241 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=,
242 expectedUnordered || !expectedEqual);
243 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, <,
244 !expectedUnordered && expectedLess);
245 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, >,
246 !expectedUnordered && !expectedLess && !expectedEqual);
247 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, <=,
248 !expectedUnordered && (expectedEqual || expectedLess));
249 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, >=,
250 !expectedUnordered && !expectedLess);
251#ifdef __cpp_lib_three_way_comparison
252 if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
253 if constexpr (std::is_convertible_v<OrderingType, std::strong_ordering>)
254 static_assert(std::is_same_v<decltype(lhs <=> rhs), std::strong_ordering>);
255 else if constexpr (std::is_convertible_v<OrderingType, std::weak_ordering>)
256 static_assert(std::is_same_v<decltype(lhs <=> rhs), std::weak_ordering>);
257 else
258 static_assert(std::is_same_v<decltype(lhs <=> rhs), std::partial_ordering>);
259
260 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, ==,
261 !expectedUnordered && expectedEqual);
262 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, !=,
263 expectedUnordered || !expectedEqual);
264 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, <,
265 !expectedUnordered && expectedLess);
266 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, >,
267 !expectedUnordered && !expectedLess && !expectedEqual);
268 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, <=,
269 !expectedUnordered && (expectedEqual || expectedLess));
270 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, >=,
271 !expectedUnordered && !expectedLess);
272 }
273#endif
274
275 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==,
276 !expectedUnordered && expectedEqual);
277 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=,
278 expectedUnordered || !expectedEqual);
279 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, <,
280 !expectedUnordered && !expectedLess && !expectedEqual);
281 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, >,
282 !expectedUnordered && expectedLess);
283 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, <=,
284 !expectedUnordered && !expectedLess);
285 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, >=,
286 !expectedUnordered && (expectedEqual || expectedLess));
287#ifdef __cpp_lib_three_way_comparison
288 if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
289 if constexpr (std::is_convertible_v<OrderingType, std::strong_ordering>)
290 static_assert(std::is_same_v<decltype(rhs <=> lhs), std::strong_ordering>);
291 else if constexpr (std::is_convertible_v<OrderingType, std::weak_ordering>)
292 static_assert(std::is_same_v<decltype(rhs <=> lhs), std::weak_ordering>);
293 else
294 static_assert(std::is_same_v<decltype(rhs <=> lhs), std::partial_ordering>);
295
296 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, ==,
297 !expectedUnordered && expectedEqual);
298 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, !=,
299 expectedUnordered || !expectedEqual);
300 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, <,
301 !expectedUnordered && !expectedLess && !expectedEqual);
302 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, >,
303 !expectedUnordered && expectedLess);
304 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, <=,
305 !expectedUnordered && !expectedLess);
306 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, >=,
307 !expectedUnordered && (expectedEqual || expectedLess));
308 }
309#endif
310}
311
312#ifdef __cpp_lib_three_way_comparison
313#undef CHECK_RUNTIME_3WAY
314#endif
315#undef CHECK_RUNTIME_LR
316#undef CHECK_RUNTIME_CREF
317
318} // namespace QTestPrivate
319
320/*!
321 \internal
322
323 A helper macro that calls QTestPrivate::testEqualityOperators(), checks the
324 test's state after the function is executed, and generates a meaningful
325 debug message with the original file and line numbers if the test has
326 failed.
327*/
328#define QT_TEST_EQUALITY_OPS(Left, Right, Expected) \
329 do { \
330 auto report = qScopeGuard([] { \
331 qDebug("testEqualityOperators(" #Left ", " #Right ", " #Expected ") " \
332 "failed in " __FILE__ " on line %d", __LINE__); \
333 }); \
334 QTestPrivate::testEqualityOperators(Left, Right, Expected); \
335 if (QTest::currentTestFailed()) \
336 return; \
337 report.dismiss(); \
338 } while (false)
339
340/*!
341 \internal
342
343 A helper macro that calls QTestPrivate::testAllComparisonOperators(), checks
344 the test's state after the function is executed, and generates a meaningful
345 debug message with the original file and line numbers if the test has
346 failed.
347*/
348#define QT_TEST_ALL_COMPARISON_OPS(Left, Right, Expected) \
349 do { \
350 auto report = qScopeGuard([] { \
351 qDebug("testAllComparisonOperators(" #Left ", " #Right ", " #Expected ") " \
352 "failed in " __FILE__ " on line %d", __LINE__); \
353 }); \
354 QTestPrivate::testAllComparisonOperators(Left, Right, Expected); \
355 if (QTest::currentTestFailed()) \
356 return; \
357 report.dismiss(); \
358 } while (false)
359
360QT_END_NAMESPACE
361
362#endif // QCOMPARISONTESTHELPER_P_H
363

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/testlib/qcomparisontesthelper_p.h