Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcomparisontesthelper_p.h
Go to the documentation of this file.
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>
32 LT, RT>;
33#endif
34
35Q_TESTLIB_EXPORT QByteArray formatTypeWithCRefImpl(QMetaType type, bool isConst,
36 bool isRef, bool isRvalueRef);
37
38template <typename T>
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>
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>
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 const char *lhsExpr, const char *rhsExpr, const char *expected,
178 const char *file, int line)
179{
180 if (expectedEqual) {
181 if (!QTest::qCompareOp<QTest::ComparisonOperation::Equal>(lhs, rhs, lhsExpr, rhsExpr, file, line))
182 QTEST_FAIL_ACTION;
183 } else {
184 if (!QTest::qCompareOp<QTest::ComparisonOperation::NotEqual>(lhs, rhs, lhsExpr, rhsExpr, file, line))
185 QTEST_FAIL_ACTION;
186 }
187
188 auto report = qScopeGuard([=] {
189 qDebug("testEqualityOperators(%s,%s,%s) failed in %s on line %d", lhsExpr, rhsExpr,
190 expected, file, line);
191 });
192 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==, expectedEqual);
193 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=, !expectedEqual);
194 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==, expectedEqual);
195 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=, !expectedEqual);
196 report.dismiss();
197}
198
199/*!
200 \internal
201 Basic testing of equality and relation operators.
202
203 The helper function tests all six relation and equality operators
204 (==, !=, <, >, <=, >=) for the \a lhs operand of type \c {LeftType} and
205 the \a rhs operand of type \c {RightType} and all six for the reverse
206 order of the operands.
207
208 If compiled in C++20 mode, also checks \c {operator<=>()} if that is
209 implemented.
210
211 When compiled in C++17 mode, the \c OrderingType must be one of
212 Qt::partial_ordering, Qt::strong_ordering, or Qt::weak_ordering.
213 In C++20 mode, also the \c {std::*_ordering} types can be used.
214
215 The \a expectedOrdering parameter provides the expected
216 relation between \a lhs and \a rhs.
217
218 \note Any test calling this method will need to check the test state after
219 doing so, if there is any later code in the test.
220
221 \code
222 QDateTime now = QDateTime::currentDateTime();
223 QDateTime later = now.addMSec(1);
224 QTestPrivate::testComparisonOperators(now, later, Qt::weak_ordering::less);
225 if (QTest:currentTestFailed())
226 return;
227 \endcode
228*/
229template <typename LeftType, typename RightType, typename OrderingType>
230void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expectedOrdering)
231{
232 constexpr bool isQOrderingType = std::is_same_v<OrderingType, Qt::partial_ordering>
233 || std::is_same_v<OrderingType, Qt::weak_ordering>
234 || std::is_same_v<OrderingType, Qt::strong_ordering>;
235#ifdef __cpp_lib_three_way_comparison
236 constexpr bool isStdOrderingType = std::is_same_v<OrderingType, std::partial_ordering>
237 || std::is_same_v<OrderingType, std::weak_ordering>
238 || std::is_same_v<OrderingType, std::strong_ordering>;
239#else
240 constexpr bool isStdOrderingType = false;
241#endif
242
243 static_assert(isQOrderingType || isStdOrderingType,
244 "Please provide, as the expectedOrdering parameter, a value "
245 "of one of the Qt::{partial,weak,strong}_ordering or "
246 "std::{partial,weak,strong}_ordering types.");
247
248 // We have all sorts of operator==() between Q*Ordering and std::*_ordering
249 // types, so we can just compare to Qt::partial_ordering.
250 const bool expectedEqual = expectedOrdering == Qt::partial_ordering::equivalent;
251 const bool expectedLess = expectedOrdering == Qt::partial_ordering::less;
252 const bool expectedUnordered = expectedOrdering == Qt::partial_ordering::unordered;
253
254 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==,
255 !expectedUnordered && expectedEqual);
256 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=,
257 expectedUnordered || !expectedEqual);
258 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, <,
259 !expectedUnordered && expectedLess);
260 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, >,
261 !expectedUnordered && !expectedLess && !expectedEqual);
262 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, <=,
263 !expectedUnordered && (expectedEqual || expectedLess));
264 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, >=,
265 !expectedUnordered && !expectedLess);
266#ifdef __cpp_lib_three_way_comparison
267 if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
268 if constexpr (std::is_convertible_v<OrderingType, std::strong_ordering>)
269 static_assert(std::is_same_v<decltype(lhs <=> rhs), std::strong_ordering>);
270 else if constexpr (std::is_convertible_v<OrderingType, std::weak_ordering>)
271 static_assert(std::is_same_v<decltype(lhs <=> rhs), std::weak_ordering>);
272 else
273 static_assert(std::is_same_v<decltype(lhs <=> rhs), std::partial_ordering>);
274
275 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, ==,
276 !expectedUnordered && expectedEqual);
277 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, !=,
278 expectedUnordered || !expectedEqual);
279 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, <,
280 !expectedUnordered && expectedLess);
281 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, >,
282 !expectedUnordered && !expectedLess && !expectedEqual);
283 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, <=,
284 !expectedUnordered && (expectedEqual || expectedLess));
285 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, >=,
286 !expectedUnordered && !expectedLess);
287 }
288#endif
289
290 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==,
291 !expectedUnordered && expectedEqual);
292 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=,
293 expectedUnordered || !expectedEqual);
294 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, <,
295 !expectedUnordered && !expectedLess && !expectedEqual);
296 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, >,
297 !expectedUnordered && expectedLess);
298 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, <=,
299 !expectedUnordered && !expectedLess);
300 CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, >=,
301 !expectedUnordered && (expectedEqual || expectedLess));
302#ifdef __cpp_lib_three_way_comparison
303 if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
304 if constexpr (std::is_convertible_v<OrderingType, std::strong_ordering>)
305 static_assert(std::is_same_v<decltype(rhs <=> lhs), std::strong_ordering>);
306 else if constexpr (std::is_convertible_v<OrderingType, std::weak_ordering>)
307 static_assert(std::is_same_v<decltype(rhs <=> lhs), std::weak_ordering>);
308 else
309 static_assert(std::is_same_v<decltype(rhs <=> lhs), std::partial_ordering>);
310
311 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, ==,
312 !expectedUnordered && expectedEqual);
313 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, !=,
314 expectedUnordered || !expectedEqual);
315 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, <,
316 !expectedUnordered && !expectedLess && !expectedEqual);
317 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, >,
318 !expectedUnordered && expectedLess);
319 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, <=,
320 !expectedUnordered && !expectedLess);
321 CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, >=,
322 !expectedUnordered && (expectedEqual || expectedLess));
323 }
324#endif
325}
326
327#ifdef __cpp_lib_three_way_comparison
328#undef CHECK_RUNTIME_3WAY
329#endif
330#undef CHECK_RUNTIME_LR
331#undef CHECK_RUNTIME_CREF
332
333} // namespace QTestPrivate
334
335/*!
336 \internal
337
338 A helper macro that calls QTestPrivate::testEqualityOperators(), checks the
339 test's state after the function is executed, and generates a meaningful
340 debug message with the original file and line numbers if the test has
341 failed.
342*/
343#define QT_TEST_EQUALITY_OPS(Left, Right, Expected)
344 do {
345 QTestPrivate::testEqualityOperators(Left, Right, Expected, #Left, #Right, #Expected,
346 __FILE__, __LINE__);
347 if (QTest::currentTestFailed())
348 return;
349 } while (false)
350
351/*!
352 \internal
353
354 A helper macro that calls QTestPrivate::testAllComparisonOperators(), checks
355 the test's state after the function is executed, and generates a meaningful
356 debug message with the original file and line numbers if the test has
357 failed.
358*/
359#define QT_TEST_ALL_COMPARISON_OPS(Left, Right, Expected)
360 do {
361 auto report = qScopeGuard([] {
362 qDebug("testAllComparisonOperators(" #Left ", " #Right ", " #Expected ") "
363 "failed in " __FILE__ " on line %d", __LINE__);
364 });
365 QTestPrivate::testAllComparisonOperators(Left, Right, Expected);
366 if (QTest::currentTestFailed())
367 return;
368 report.dismiss();
369 } while (false)
370
371QT_END_NAMESPACE
372
373#endif // QCOMPARISONTESTHELPER_P_H
Combined button and popup list for selecting options.
QByteArray formatTypeWithCRef()
void testEqualityOperators(LeftType lhs, RightType rhs, bool expectedEqual, const char *lhsExpr, const char *rhsExpr, const char *expected, const char *file, int line)
void testAllComparisonOperatorsCompile()
void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expectedOrdering)
QByteArray formatTypeWithCRefImpl(QMetaType type, bool isConst, bool isRef, bool isRvalueRef)
#define FOR_EACH_CREF(Func, Left, Right, Op, Result)
#define CHECK_RUNTIME_CREF(Func, Left, Right, Op, Expected)