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

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