@micro-os-plus/micro-test-plus 2.1.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,17 @@
1
1
  /*
2
2
  * This file is part of the µOS++ distribution.
3
- * (https://github.com/micro-os-plus)
3
+ * (https://github.com/micro-os-plus/)
4
4
  * Copyright (c) 2021 Liviu Ionescu.
5
5
  *
6
6
  * Permission to use, copy, modify, and/or distribute this software
7
7
  * for any purpose is hereby granted, under the terms of the MIT license.
8
+ *
9
+ * If a copy of the license was not distributed with this file, it can
10
+ * be obtained from <https://opensource.org/licenses/MIT/>.
11
+ *
12
+ * Major parts of the code are inspired from v1.1.8 of the Boost UT project,
13
+ * released under the terms of the Boost Version 1.0 Software License,
14
+ * which can be obtained from <https://www.boost.org/LICENSE_1_0.txt>.
8
15
  */
9
16
 
10
17
  #ifndef MICRO_TEST_PLUS_MICRO_TEST_PLUS_H_
@@ -16,130 +23,509 @@
16
23
 
17
24
  // ----------------------------------------------------------------------------
18
25
 
26
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
27
+ #include <micro-os-plus/config.h>
28
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
29
+
30
+ #include "reflection.h"
31
+ #include "math.h"
32
+ #include "type-traits.h"
33
+ #include "literals.h"
34
+ #include "test-suite.h"
35
+ #include "test-runner.h"
36
+ #include "test-reporter.h"
37
+ #include "detail.h"
38
+
39
+ // ----------------------------------------------------------------------------
40
+
19
41
  #if defined(__GNUC__)
20
42
  #pragma GCC diagnostic push
43
+ #pragma GCC diagnostic ignored "-Wpadded"
44
+ #pragma GCC diagnostic ignored "-Waggregate-return"
21
45
  #if defined(__clang__)
22
46
  #pragma clang diagnostic ignored "-Wc++98-compat"
47
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
48
+ #pragma clang diagnostic ignored "-Wctad-maybe-unsupported"
23
49
  #endif
24
50
  #endif
25
51
 
26
- namespace micro_os_plus
52
+ namespace micro_os_plus::micro_test_plus
27
53
  {
28
- namespace micro_test_plus // `micro-test-plus` is shortened to `mtp`.
54
+ // --------------------------------------------------------------------------
55
+
56
+ extern test_runner runner;
57
+ extern test_reporter reporter;
58
+ extern test_suite_base* current_test_suite;
59
+
60
+ // --------------------------------------------------------------------------
61
+ // Public API.
62
+
63
+ /**
64
+ * @brief Initialize the test framework.
65
+ */
66
+ void
67
+ initialize (int argc, char* argv[], const char* name = "Main");
68
+
69
+ /**
70
+ * @brief Define and execute a test case.
71
+ */
72
+ template <typename Callable_T, typename... Args_T>
73
+ void
74
+ test_case (const char* name, Callable_T&& func, Args_T&&... arguments);
75
+
76
+ /**
77
+ * @brief Trigger the execution of the globally registered test suites
78
+ * and return the test result.
79
+ */
80
+ [[nodiscard]] int
81
+ exit_code (void);
82
+
83
+ /**
84
+ * @brief Evaluate a generic condition. The expression must use
85
+ * the provided `eq(), ne(), lt(), le(), gt(), ge()` comparators,
86
+ * or, if the custom operators are used, to include custom type
87
+ * operands, otherwise support for identifying the failed check
88
+ * is not provided.
89
+ *
90
+ * The template is usable only for expressions that evaluate to
91
+ * a boolean or use custom comparators/operators derived from the
92
+ * local `op` type.
93
+ */
94
+ template <
95
+ class Expr_T,
96
+ type_traits::requires_t<
97
+ type_traits::is_op_v<
98
+ Expr_T> or type_traits::is_convertible_v<Expr_T, bool>> = 0>
99
+ constexpr auto
100
+ expect (const Expr_T& expr, const reflection::source_location& sl
101
+ = reflection::source_location::current ())
29
102
  {
30
-
31
- #if defined(__GNUC__)
32
- #pragma GCC diagnostic push
33
- #pragma GCC diagnostic ignored "-Wpadded"
103
+ return detail::deferred_reporter<Expr_T>{ expr, false, sl };
104
+ }
105
+
106
+ /**
107
+ * @brief Check a condition and, if false, abort.
108
+ *
109
+ * The template is usable only for expressions that evaluate to
110
+ * a boolean or use custom comparators/operators derived from the
111
+ * local `op` type.
112
+ */
113
+ template <
114
+ class Expr_T,
115
+ type_traits::requires_t<
116
+ type_traits::is_op_v<
117
+ Expr_T> or type_traits::is_convertible_v<Expr_T, bool>> = 0>
118
+ constexpr auto
119
+ assume (const Expr_T& expr, const reflection::source_location& sl
120
+ = reflection::source_location::current ())
121
+ {
122
+ return detail::deferred_reporter<Expr_T>{ expr, true, sl };
123
+ }
124
+
125
+ // --------------------------------------------------------------------------
126
+
127
+ #if defined(__cpp_exceptions)
128
+ /**
129
+ * @brief Check if a callable throws a specific exception.
130
+ */
131
+ template <class Exception_T, class Callable_T>
132
+ [[nodiscard]] constexpr auto
133
+ throws (const Callable_T& func)
134
+ {
135
+ return detail::throws_<Callable_T, Exception_T>{ func };
136
+ }
137
+
138
+ /**
139
+ * @brief Check if a callable throws an exception (any exception).
140
+ */
141
+ template <class Callable_T>
142
+ [[nodiscard]] constexpr auto
143
+ throws (const Callable_T& func)
144
+ {
145
+ return detail::throws_<Callable_T>{ func };
146
+ }
147
+
148
+ /**
149
+ * @brief Check if a callable doesn't throw an exception.
150
+ */
151
+ template <class Callable_T>
152
+ [[nodiscard]] constexpr auto
153
+ nothrow (const Callable_T& func)
154
+ {
155
+ return detail::nothrow_{ func };
156
+ }
34
157
  #endif
35
158
 
36
- class session
37
- {
38
- public:
39
- session ();
40
-
41
- session (int argc, char* argv[]);
159
+ // --------------------------------------------------------------------------
42
160
 
43
- session (const session&) = delete;
44
- session (session&&) = delete;
45
- session&
46
- operator= (const session&)
47
- = delete;
48
- session&
49
- operator= (session&&)
50
- = delete;
51
-
52
- ~session () = default;
53
-
54
- void
55
- init (int argc, char* argv[]);
56
-
57
- void
58
- start_suite (const char* name);
59
-
60
- void
61
- run_test_case (void (*f) (session&), const char* name);
62
-
63
- void
64
- pass (const char* message, const char* file = nullptr, int line = 0);
65
-
66
- void
67
- fail (const char* message, const char* file = nullptr, int line = 0);
68
-
69
- void
70
- expect_true (bool condition, const char* message,
71
- const char* file = nullptr, int line = 0);
72
-
73
- void
74
- expect_eq (int actual, int expected, const char* message,
75
- const char* file = nullptr, int line = 0);
76
-
77
- void
78
- expect_eq (long actual, long expected, const char* message,
79
- const char* file = nullptr, int line = 0);
80
-
81
- void
82
- expect_eq (const char* actual, const char* expected, const char* message,
83
- const char* file = nullptr, int line = 0);
84
-
85
- int
86
- result (void);
87
-
88
- inline int
89
- passed (void)
90
- {
91
- return passed_;
92
- }
93
-
94
- inline int
95
- failed (void)
96
- {
97
- return failed_;
98
- }
99
-
100
- inline int
101
- test_cases (void)
102
- {
103
- return test_cases_;
104
- }
105
-
106
- protected:
107
- void
108
- print_where_ (const char* format, const char* file, int line);
109
-
110
- protected:
111
- int argc_;
112
- char** argv_;
161
+ /**
162
+ * @brief Generic equality comparator. Matches any
163
+ * non-pointer type.
164
+ */
165
+ template <class Lhs_T, class Rhs_T>
166
+ [[nodiscard]] constexpr auto
167
+ eq (const Lhs_T& lhs, const Rhs_T& rhs)
168
+ {
169
+ return detail::eq_{ lhs, rhs };
170
+ }
171
+
172
+ /**
173
+ * @brief Pointer equality comparator. Matches pointers
174
+ * to any types.
175
+ */
176
+ template <class Lhs_T, class Rhs_T>
177
+ [[nodiscard]] constexpr auto
178
+ eq (Lhs_T* lhs, Rhs_T* rhs)
179
+ {
180
+ return detail::eq_{ lhs, rhs };
181
+ }
182
+
183
+ /**
184
+ * @brief Generic non-equality comparator.
185
+ */
186
+ template <class Lhs_T, class Rhs_T>
187
+ [[nodiscard]] constexpr auto
188
+ ne (const Lhs_T& lhs, const Rhs_T& rhs)
189
+ {
190
+ return detail::ne_{ lhs, rhs };
191
+ }
192
+
193
+ /**
194
+ * @brief Pointer non-equality comparator.
195
+ */
196
+ template <class Lhs_T, class Rhs_T>
197
+ [[nodiscard]] constexpr auto
198
+ ne (Lhs_T* lhs, Rhs_T* rhs)
199
+ {
200
+ return detail::ne_{ lhs, rhs };
201
+ }
202
+
203
+ /**
204
+ * @brief Generic greater than comparator.
205
+ */
206
+ template <class Lhs_T, class Rhs_T>
207
+ [[nodiscard]] constexpr auto
208
+ gt (const Lhs_T& lhs, const Rhs_T& rhs)
209
+ {
210
+ return detail::gt_{ lhs, rhs };
211
+ }
212
+
213
+ /**
214
+ * @brief Pointer greater than comparator.
215
+ */
216
+ template <class Lhs_T, class Rhs_T>
217
+ [[nodiscard]] constexpr auto
218
+ gt (Lhs_T* lhs, Rhs_T* rhs)
219
+ {
220
+ return detail::gt_{ lhs, rhs };
221
+ }
222
+
223
+ /**
224
+ * @brief Generic greater than or equal comparator.
225
+ */
226
+ template <class Lhs_T, class Rhs_T>
227
+ [[nodiscard]] constexpr auto
228
+ ge (const Lhs_T& lhs, const Rhs_T& rhs)
229
+ {
230
+ return detail::ge_{ lhs, rhs };
231
+ }
232
+
233
+ /**
234
+ * @brief Pointer greater than or equal comparator.
235
+ */
236
+ template <class Lhs_T, class Rhs_T>
237
+ [[nodiscard]] constexpr auto
238
+ ge (Lhs_T* lhs, Rhs_T* rhs)
239
+ {
240
+ return detail::ge_{ lhs, rhs };
241
+ }
242
+
243
+ /**
244
+ * @brief Generic less than comparator.
245
+ */
246
+ template <class Lhs_T, class Rhs_T>
247
+ [[nodiscard]] constexpr auto
248
+ lt (const Lhs_T& lhs, const Rhs_T& rhs)
249
+ {
250
+ return detail::lt_{ lhs, rhs };
251
+ }
252
+
253
+ /**
254
+ * @brief Generic less than comparator.
255
+ */
256
+ template <class Lhs_T, class Rhs_T>
257
+ [[nodiscard]] constexpr auto
258
+ lt (Lhs_T* lhs, Rhs_T* rhs)
259
+ {
260
+ return detail::lt_{ lhs, rhs };
261
+ }
262
+
263
+ /**
264
+ * @brief Generic less than or equal comparator.
265
+ */
266
+ template <class Lhs_T, class Rhs_T>
267
+ [[nodiscard]] constexpr auto
268
+ le (const Lhs_T& lhs, const Rhs_T& rhs)
269
+ {
270
+ return detail::le_{ lhs, rhs };
271
+ }
272
+
273
+ /**
274
+ * @brief Generic less than or equal comparator.
275
+ */
276
+ template <class Lhs_T, class Rhs_T>
277
+ [[nodiscard]] constexpr auto
278
+ le (Lhs_T* lhs, Rhs_T* rhs)
279
+ {
280
+ return detail::le_{ lhs, rhs };
281
+ }
282
+
283
+ /**
284
+ * @brief Generic logical not. The underscore is intentional,
285
+ * to differentiate from the standard operator.
286
+ */
287
+ template <class Expr_T>
288
+ [[nodiscard]] constexpr auto
289
+ _not (const Expr_T& expr)
290
+ {
291
+ return detail::not_{ expr };
292
+ }
293
+
294
+ /**
295
+ * @brief Generic logical and. The underscore is intentional,
296
+ * to differentiate from the standard operator.
297
+ */
298
+ template <class Lhs_T, class Rhs_T>
299
+ [[nodiscard]] constexpr auto
300
+ _and (const Lhs_T& lhs, const Rhs_T& rhs)
301
+ {
302
+ return detail::and_{ lhs, rhs };
303
+ }
304
+
305
+ /**
306
+ * @brief Generic logical or. The underscore is intentional,
307
+ * to differentiate from the standard operator.
308
+ */
309
+ template <class Lhs_T, class Rhs_T>
310
+ [[nodiscard]] constexpr auto
311
+ _or (const Lhs_T& lhs, const Rhs_T& rhs)
312
+ {
313
+ return detail::or_{ lhs, rhs };
314
+ }
315
+
316
+ /**
317
+ * @brief Generic mutator, to remove const from any type.
318
+ */
319
+ template <class T>
320
+ [[nodiscard]] constexpr auto
321
+ mut (const T& t) noexcept -> T&
322
+ {
323
+ return const_cast<T&> (t);
324
+ }
325
+
326
+ // --------------------------------------------------------------------------
327
+
328
+ /**
329
+ * @brief Separate namespace with custom operators.
330
+ *
331
+ * @warning Please note that they
332
+ * may interfere with other operators existing in the tested application.
333
+ *
334
+ * To minimise the interferences, these operators are recognised only
335
+ * for specific types, and generally require constants to be
336
+ * suffixed with literals (like `1_i`), and dynamic values to be
337
+ * casted to the custom types (like `_i(...)`).
338
+ */
339
+ namespace operators
340
+ {
341
+ /**
342
+ * @brief Equality operator for string_view objects.
343
+ */
344
+ [[nodiscard]] constexpr auto
345
+ operator== (std::string_view lhs, std::string_view rhs)
346
+ {
347
+ return detail::eq_{ lhs, rhs };
348
+ }
349
+
350
+ /**
351
+ * @brief Non-equality operator for string_view objects.
352
+ */
353
+ [[nodiscard]] constexpr auto
354
+ operator!= (std::string_view lhs, std::string_view rhs)
355
+ {
356
+ return detail::ne_{ lhs, rhs };
357
+ }
358
+
359
+ /**
360
+ * @brief Equality operator for containers.
361
+ */
362
+ template <class T,
363
+ type_traits::requires_t<type_traits::is_container_v<T>> = 0>
364
+ [[nodiscard]] constexpr auto
365
+ operator== (T&& lhs, T&& rhs)
366
+ {
367
+ return detail::eq_{ static_cast<T&&> (lhs), static_cast<T&&> (rhs) };
368
+ }
369
+
370
+ /**
371
+ * @brief Non-equality operator for containers.
372
+ */
373
+ template <class T,
374
+ type_traits::requires_t<type_traits::is_container_v<T>> = 0>
375
+ [[nodiscard]] constexpr auto
376
+ operator!= (T&& lhs, T&& rhs)
377
+ {
378
+ return detail::ne_{ static_cast<T&&> (lhs), static_cast<T&&> (rhs) };
379
+ }
380
+
381
+ /**
382
+ * @brief Equality operator. It matches only if at least one
383
+ * operand is of local type (derived from local `op`).
384
+ */
385
+ template <
386
+ class Lhs_T, class Rhs_T,
387
+ type_traits::requires_t<
388
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
389
+ [[nodiscard]] constexpr auto
390
+ operator== (const Lhs_T& lhs, const Rhs_T& rhs)
391
+ {
392
+ return detail::eq_{ lhs, rhs };
393
+ }
394
+
395
+ /**
396
+ * @brief Non-equality operator. It matches only if at least one
397
+ * operand is of local type (derived from local `op`).
398
+ */
399
+ template <
400
+ class Lhs_T, class Rhs_T,
401
+ type_traits::requires_t<
402
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
403
+ [[nodiscard]] constexpr auto
404
+ operator!= (const Lhs_T& lhs, const Rhs_T& rhs)
405
+ {
406
+ return detail::ne_{ lhs, rhs };
407
+ }
408
+
409
+ /**
410
+ * @brief Greater than operator. It matches only if at least one
411
+ * operand is of local type (derived from local `op`).
412
+ */
413
+ template <
414
+ class Lhs_T, class Rhs_T,
415
+ type_traits::requires_t<
416
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
417
+ [[nodiscard]] constexpr auto
418
+ operator> (const Lhs_T& lhs, const Rhs_T& rhs)
419
+ {
420
+ return detail::gt_{ lhs, rhs };
421
+ }
422
+
423
+ /**
424
+ * @brief Greater than or equal operator. It matches only if at least one
425
+ * operand is of local type (derived from local `op`).
426
+ */
427
+ template <
428
+ class Lhs_T, class Rhs_T,
429
+ type_traits::requires_t<
430
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
431
+ [[nodiscard]] constexpr auto
432
+ operator>= (const Lhs_T& lhs, const Rhs_T& rhs)
433
+ {
434
+ return detail::ge_{ lhs, rhs };
435
+ }
436
+
437
+ /**
438
+ * @brief Less than operator. It matches only if at least one
439
+ * operand is of local type (derived from local `op`).
440
+ */
441
+ template <
442
+ class Lhs_T, class Rhs_T,
443
+ type_traits::requires_t<
444
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
445
+ [[nodiscard]] constexpr auto
446
+ operator< (const Lhs_T& lhs, const Rhs_T& rhs)
447
+ {
448
+ return detail::lt_{ lhs, rhs };
449
+ }
450
+
451
+ /**
452
+ * @brief Less than or equal operator. It matches only if at least one
453
+ * operand is of local type (derived from local `op`).
454
+ */
455
+ template <
456
+ class Lhs_T, class Rhs_T,
457
+ type_traits::requires_t<
458
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
459
+ [[nodiscard]] constexpr auto
460
+ operator<= (const Lhs_T& lhs, const Rhs_T& rhs)
461
+ {
462
+ return detail::le_{ lhs, rhs };
463
+ }
464
+
465
+ /**
466
+ * @brief Logical `and` operator. It matches only if at least one
467
+ * operand is of local type (derived from local `op`).
468
+ */
469
+ template <
470
+ class Lhs_T, class Rhs_T,
471
+ type_traits::requires_t<
472
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
473
+ [[nodiscard]] constexpr auto
474
+ operator and (const Lhs_T& lhs, const Rhs_T& rhs)
475
+ {
476
+ return detail::and_{ lhs, rhs };
477
+ }
478
+
479
+ /**
480
+ * @brief Logical `or` operator. It matches only if at least one
481
+ * operand is of local type (derived from local `op`).
482
+ */
483
+ template <
484
+ class Lhs_T, class Rhs_T,
485
+ type_traits::requires_t<
486
+ type_traits::is_op_v<Lhs_T> or type_traits::is_op_v<Rhs_T>> = 0>
487
+ [[nodiscard]] constexpr auto
488
+ operator or (const Lhs_T& lhs, const Rhs_T& rhs)
489
+ {
490
+ return detail::or_{ lhs, rhs };
491
+ }
492
+
493
+ /**
494
+ * @brief Logical `not` operator. It matches only if the
495
+ * operand is of local type (derived from local `op`).
496
+ */
497
+ template <class T, type_traits::requires_t<type_traits::is_op_v<T>> = 0>
498
+ [[nodiscard]] constexpr auto
499
+ operator not (const T& t)
500
+ {
501
+ return detail::not_{ t };
502
+ }
503
+ } // namespace operators
113
504
 
114
- const char* name_;
505
+ namespace utility
506
+ {
507
+ [[nodiscard]] bool
508
+ is_match (std::string_view input, std::string_view pattern);
115
509
 
116
- int passed_;
117
- int failed_;
118
- int test_cases_;
119
- };
510
+ template <class T, class Delim_T>
511
+ [[nodiscard]] auto
512
+ split (T input, Delim_T delim) -> std::vector<T>;
120
513
 
121
- #if defined(__GNUC__)
122
- #pragma GCC diagnostic pop
123
- #endif
514
+ } // namespace utility
124
515
 
125
- } // namespace micro_test_plus
126
- } // namespace micro_os_plus
516
+ // --------------------------------------------------------------------------
517
+ } // namespace micro_os_plus::micro_test_plus
127
518
 
128
519
  #if defined(__GNUC__)
129
520
  #pragma GCC diagnostic pop
130
521
  #endif
131
522
 
132
- // ----------------------------------------------------------------------------
523
+ // ===== Inline & template implementations ====================================
133
524
 
134
- // Macros are only a convenience to pass the file name and line number,
135
- // otherwise the direct methods can be used as well.
136
- #define MTP_EXPECT_EQ(t, actual, expected, message) \
137
- t.expect_eq (actual, expected, message, __FILE__, __LINE__)
138
- #define MTP_EXPECT_TRUE(t, condition, message) \
139
- t.expect_true (condition, message, __FILE__, __LINE__)
525
+ #include "test-reporter-inlines.h"
140
526
 
141
- #define MTP_PASS(t, message) t.pass (message, __FILE__, __LINE__)
142
- #define MTP_FAIL(t, message) t.fail (message, __FILE__, __LINE__)
527
+ // All other inlines.
528
+ #include "inlines.h"
143
529
 
144
530
  // ----------------------------------------------------------------------------
145
531