@micro-os-plus/micro-test-plus 3.3.0 → 4.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +339 -2
  2. package/CMakeLists.txt +79 -23
  3. package/README.md +1 -1
  4. package/config/xcdl-build.json +11 -4
  5. package/include/micro-os-plus/micro-test-plus/deferred-reporter.h +292 -0
  6. package/include/micro-os-plus/micro-test-plus/detail.h +462 -1076
  7. package/include/micro-os-plus/micro-test-plus/exceptions.h +126 -0
  8. package/include/micro-os-plus/micro-test-plus/function-comparators.h +10 -7
  9. package/include/micro-os-plus/micro-test-plus/inlines/{details-inlines.h → deferred-reporter-inlines.h} +49 -22
  10. package/include/micro-os-plus/micro-test-plus/inlines/function-comparators-inlines.h +67 -4
  11. package/include/micro-os-plus/micro-test-plus/inlines/literals-inlines.h +3 -6
  12. package/include/micro-os-plus/micro-test-plus/inlines/math-inlines.h +21 -15
  13. package/include/micro-os-plus/micro-test-plus/inlines/reflection-inlines.h +35 -17
  14. package/include/micro-os-plus/micro-test-plus/inlines/{test-reporter-inlines.h → reporter-inlines.h} +176 -106
  15. package/include/micro-os-plus/micro-test-plus/inlines/{test-suite-inlines.h → runner-inlines.h} +41 -43
  16. package/include/micro-os-plus/micro-test-plus/inlines/test-inlines.h +369 -0
  17. package/include/micro-os-plus/micro-test-plus/inlines/utility-inlines.h +126 -0
  18. package/include/micro-os-plus/micro-test-plus/literals.h +4 -3
  19. package/include/micro-os-plus/micro-test-plus/math.h +9 -6
  20. package/include/micro-os-plus/micro-test-plus/operators.h +38 -44
  21. package/include/micro-os-plus/micro-test-plus/reflection.h +15 -4
  22. package/include/micro-os-plus/micro-test-plus/{test-reporter-basic.h → reporter-human.h} +72 -72
  23. package/include/micro-os-plus/micro-test-plus/{test-reporter-tap.h → reporter-tap.h} +69 -69
  24. package/include/micro-os-plus/micro-test-plus/{test-reporter.h → reporter.h} +296 -200
  25. package/include/micro-os-plus/micro-test-plus/runner-totals.h +264 -0
  26. package/include/micro-os-plus/micro-test-plus/runner.h +453 -0
  27. package/include/micro-os-plus/micro-test-plus/test.h +1069 -0
  28. package/include/micro-os-plus/micro-test-plus/timings.h +366 -0
  29. package/include/micro-os-plus/micro-test-plus/type-traits.h +239 -545
  30. package/include/micro-os-plus/micro-test-plus/utility.h +135 -0
  31. package/include/micro-os-plus/micro-test-plus.h +25 -228
  32. package/meson.build +10 -6
  33. package/package.json +1 -1
  34. package/src/deferred-reporter.cpp +118 -0
  35. package/src/reflection.cpp +95 -0
  36. package/src/reporter-human.cpp +822 -0
  37. package/src/reporter-tap.cpp +782 -0
  38. package/src/reporter.cpp +676 -0
  39. package/src/runner-totals.cpp +95 -0
  40. package/src/runner.cpp +563 -0
  41. package/src/test.cpp +496 -0
  42. package/src/timings.cpp +209 -0
  43. package/src/utility.cpp +163 -0
  44. package/.cmake-format.yaml +0 -11
  45. package/include/micro-os-plus/micro-test-plus/inlines/micro-test-plus-inlines.h +0 -313
  46. package/include/micro-os-plus/micro-test-plus/test-runner.h +0 -281
  47. package/include/micro-os-plus/micro-test-plus/test-suite.h +0 -492
  48. package/src/micro-test-plus.cpp +0 -316
  49. package/src/test-reporter-basic.cpp +0 -466
  50. package/src/test-reporter-tap.cpp +0 -530
  51. package/src/test-reporter.cpp +0 -399
  52. package/src/test-runner.cpp +0 -311
  53. package/src/test-suite.cpp +0 -304
package/src/test.cpp ADDED
@@ -0,0 +1,496 @@
1
+ /*
2
+ * This file is part of the µOS++ project (https://micro-os-plus.github.io/).
3
+ * Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
4
+ *
5
+ * Permission to use, copy, modify, and/or distribute this software for any
6
+ * purpose is hereby granted, under the terms of the MIT license.
7
+ *
8
+ * If a copy of the license was not distributed with this file, it can be
9
+ * obtained from https://opensource.org/licenses/mit.
10
+ *
11
+ * Major parts of the code are inspired from v1.1.8 of the Boost UT project,
12
+ * released under the terms of the Boost Version 1.0 Software License,
13
+ * which can be obtained from https://www.boost.org/LICENSE_1_0.txt.
14
+ */
15
+
16
+ // ----------------------------------------------------------------------------
17
+
18
+ /**
19
+ * @file
20
+ * @brief C++ source file with implementations for the µTest++ test suite
21
+ * methods.
22
+ *
23
+ * @details
24
+ * This source file contains the core implementations for the test suite
25
+ * facilities of the µTest++ framework. It provides the logic for constructing,
26
+ * registering, and managing test suites and their associated test cases. The
27
+ * implementation covers initialisation and clean-up routines, execution of
28
+ * test suites and test cases, tracking of successful and failed checks, and
29
+ * integration with the test reporter for structured output.
30
+ *
31
+ * The design ensures that test suites are non-copyable and non-movable,
32
+ * maintaining unique ownership and consistent state. Flexible support for
33
+ * callable objects enables a wide range of test suite definitions,
34
+ * facilitating expressive and maintainable test organisation across embedded
35
+ * and general C++ projects.
36
+ *
37
+ * All definitions reside within the `micro_os_plus::micro_test_plus`
38
+ * namespace, ensuring clear separation from user code and minimising the risk
39
+ * of naming conflicts.
40
+ *
41
+ * This file must be included when building the µTest++ library.
42
+ */
43
+
44
+ // ----------------------------------------------------------------------------
45
+
46
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
47
+ #include <micro-os-plus/config.h>
48
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
49
+
50
+ #include <micro-os-plus/micro-test-plus.h>
51
+ #include <micro-os-plus/diag/trace.h>
52
+
53
+ // ----------------------------------------------------------------------------
54
+
55
+ #if defined(__GNUC__)
56
+ #if defined(__clang__)
57
+ #pragma clang diagnostic ignored "-Wc++98-compat"
58
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
59
+ #else // GCC only
60
+ #pragma GCC diagnostic ignored "-Wredundant-tags"
61
+ #pragma GCC diagnostic ignored "-Wsuggest-final-types"
62
+ #pragma GCC diagnostic ignored "-Wsuggest-final-methods"
63
+ #endif
64
+ #endif
65
+
66
+ // ==========================================================================
67
+
68
+ namespace micro_os_plus::micro_test_plus
69
+ {
70
+ // ==========================================================================
71
+
72
+ /**
73
+ * @details
74
+ * Stores the supplied @p name pointer, which is expected to point to
75
+ * a string with a lifetime exceeding that of this instance. If
76
+ * tracing is enabled, the name is output for diagnostic purposes.
77
+ */
78
+ test_node::test_node (const char* name) : name_{ name }
79
+ {
80
+ #if defined(MICRO_OS_PLUS_TRACE) \
81
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
82
+ #if defined(__GNUC__)
83
+ #pragma GCC diagnostic push
84
+ #if defined(__clang__)
85
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
86
+ #endif
87
+ #endif
88
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name);
89
+ #if defined(__GNUC__)
90
+ #pragma GCC diagnostic pop
91
+ #endif
92
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
93
+ }
94
+
95
+ /**
96
+ * @details
97
+ * No resources are owned by `test_node`; the destructor performs no
98
+ * explicit clean-up. If tracing is enabled, the node name is output
99
+ * for diagnostic purposes.
100
+ */
101
+ test_node::~test_node ()
102
+ {
103
+ #if defined(MICRO_OS_PLUS_TRACE) \
104
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
105
+ #if defined(__GNUC__)
106
+ #pragma GCC diagnostic push
107
+ #if defined(__clang__)
108
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
109
+ #endif
110
+ #endif
111
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
112
+ #if defined(__GNUC__)
113
+ #pragma GCC diagnostic pop
114
+ #endif
115
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
116
+ }
117
+
118
+ // ==========================================================================
119
+
120
+ /**
121
+ * @details
122
+ * The constructor initialises a new instance of the `runnable_base` class
123
+ * with the specified name. It sets up the internal state required for
124
+ * managing test cases within the suite. If tracing is enabled, the function
125
+ * signature is output for diagnostic purposes. The default test suite does
126
+ * not require explicit registration, ensuring seamless integration within
127
+ * the µTest++ framework and supporting organised test management across all
128
+ * files and folders.
129
+ */
130
+ runnable_base::runnable_base (const char* name, class runner& runner,
131
+ size_t own_index)
132
+ : test_node{ name }, runner_{ runner }, own_index_{ own_index }
133
+ {
134
+ #if defined(MICRO_OS_PLUS_TRACE) \
135
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
136
+ #if defined(__GNUC__)
137
+ #pragma GCC diagnostic push
138
+ #if defined(__clang__)
139
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
140
+ #endif
141
+ #endif
142
+ trace::printf ("%s '%s' %zu\n", __PRETTY_FUNCTION__, name, own_index_);
143
+ #if defined(__GNUC__)
144
+ #pragma GCC diagnostic pop
145
+ #endif
146
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
147
+ }
148
+
149
+ /**
150
+ * @details
151
+ * The destructor releases any resources associated with the
152
+ * `runnable_base` instance. It ensures that the test suite is properly
153
+ * cleaned up after execution, supporting robust and reliable test management
154
+ * across all files and folders within the µTest++ framework.
155
+ */
156
+ runnable_base::~runnable_base ()
157
+ {
158
+ #if defined(MICRO_OS_PLUS_TRACE) \
159
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
160
+ #if defined(__GNUC__)
161
+ #pragma GCC diagnostic push
162
+ #if defined(__clang__)
163
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
164
+ #endif
165
+ #endif
166
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
167
+ #if defined(__GNUC__)
168
+ #pragma GCC diagnostic pop
169
+ #endif
170
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
171
+
172
+ // children_subtests_ holds unique_ptrs; destroyed automatically.
173
+ }
174
+
175
+ /**
176
+ * @details
177
+ * Delegates immediately to `runner_.reporter()`, returning the
178
+ * reporter associated with the owning runner instance.
179
+ */
180
+ [[nodiscard]] reporter&
181
+ runnable_base::reporter (void) const noexcept
182
+ {
183
+ return runner_.reporter ();
184
+ }
185
+
186
+ /**
187
+ * @details
188
+ * Delegates immediately to `runner_.abort()`, passing the supplied
189
+ * source location so that the error message identifies the call site
190
+ * before the process is terminated via `::abort()`.
191
+ */
192
+ [[noreturn]] void
193
+ runnable_base::abort (const reflection::source_location& sl)
194
+ {
195
+ runner_.abort (sl);
196
+ }
197
+
198
+ /**
199
+ * @details
200
+ * Transfers ownership of @p child_test into `children_subtests_` and
201
+ * immediately invokes `subtest::run()` on the newly stored subtest.
202
+ * The parent's executed-subtest counter is then incremented. The
203
+ * child's check counters are intentionally not merged into the parent
204
+ * totals; each subtest reports only its own counters. The child's
205
+ * totals are, however, accumulated into @p suite so that the suite
206
+ * summary reflects all checks performed by its subtests.
207
+ */
208
+ void
209
+ runnable_base::after_subtest_create_ (
210
+ std::unique_ptr<class subtest> child_test, suite& suite)
211
+ {
212
+ // Transfer ownership into the vector.
213
+ children_subtests_.push_back (std::move (child_test));
214
+
215
+ // Run the child test case immediately.
216
+ class subtest& subtest = *children_subtests_.back ();
217
+ subtest.run ();
218
+
219
+ // This test executed one more subtest.
220
+ #if defined(MICRO_OS_PLUS_TRACE) \
221
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
222
+ #if defined(__GNUC__)
223
+ #pragma GCC diagnostic push
224
+ #if defined(__clang__)
225
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
226
+ #endif
227
+ #endif
228
+ trace::printf ("%s subtest '%s' executed one more subtest\n",
229
+ __PRETTY_FUNCTION__, name ());
230
+ #if defined(__GNUC__)
231
+ #pragma GCC diagnostic pop
232
+ #endif
233
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
234
+ totals ().increment_executed_subtests ();
235
+ // Do not accumulate the totals from the child test into the current test
236
+ // totals, each subtest shows only its counters.
237
+
238
+ #if defined(MICRO_OS_PLUS_TRACE) \
239
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
240
+ #if defined(__GNUC__)
241
+ #pragma GCC diagnostic push
242
+ #if defined(__clang__)
243
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
244
+ #endif
245
+ #endif
246
+ trace::printf ("%s suite '%s' totals\n", __PRETTY_FUNCTION__,
247
+ suite.name ());
248
+ #if defined(__GNUC__)
249
+ #pragma GCC diagnostic pop
250
+ #endif
251
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
252
+
253
+ // Accumulate the totals from the child test into the suite totals.
254
+ suite.totals () += subtest.totals ();
255
+ }
256
+
257
+ // ==========================================================================
258
+
259
+ /**
260
+ * @details
261
+ * The destructor releases any resources associated with the
262
+ * `test` instance. If tracing is enabled, it outputs the
263
+ * function signature for diagnostic purposes. This ensures that the test
264
+ * suite is properly cleaned up after execution, supporting robust and
265
+ * reliable test management across all files and folders within the µTest++
266
+ * framework.
267
+ */
268
+ subtest::~subtest ()
269
+ {
270
+ #if defined(MICRO_OS_PLUS_TRACE) \
271
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
272
+ #if defined(__GNUC__)
273
+ #pragma GCC diagnostic push
274
+ #if defined(__clang__)
275
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
276
+ #endif
277
+ #endif
278
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
279
+ #if defined(__GNUC__)
280
+ #pragma GCC diagnostic pop
281
+ #endif
282
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
283
+ }
284
+
285
+ /**
286
+ * @details
287
+ * Invokes the stored callable with the `Self_T` instance, surrounded by
288
+ * reporter calls to begin and end the subtest.
289
+ */
290
+ void
291
+ subtest::run (void)
292
+ {
293
+ #if defined(MICRO_OS_PLUS_TRACE) \
294
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
295
+ #if defined(__GNUC__)
296
+ #pragma GCC diagnostic push
297
+ #if defined(__clang__)
298
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
299
+ #endif
300
+ #endif
301
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
302
+ #if defined(__GNUC__)
303
+ #pragma GCC diagnostic pop
304
+ #endif
305
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
306
+
307
+ class reporter& reporter = this->reporter ();
308
+
309
+ // For now, subtests do not record the time.
310
+ // this->timings.timestamp_begin ();
311
+ reporter.begin_subtest (*this);
312
+
313
+ // Invoke the callable, passing the self reference followed by the variadic
314
+ // arguments.
315
+ callable_ (*this);
316
+
317
+ // this->timings.timestamp_end ();
318
+ reporter.end_subtest (*this);
319
+ }
320
+
321
+ // ==========================================================================
322
+
323
+ /**
324
+ * @details
325
+ * No resources are owned directly by `suite`; the destructor performs
326
+ * no explicit clean-up. If tracing is enabled, the suite name is
327
+ * output for diagnostic purposes.
328
+ */
329
+ suite::~suite ()
330
+ {
331
+ #if defined(MICRO_OS_PLUS_TRACE) \
332
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
333
+ #if defined(__GNUC__)
334
+ #pragma GCC diagnostic push
335
+ #if defined(__clang__)
336
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
337
+ #endif
338
+ #endif
339
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
340
+ #if defined(__GNUC__)
341
+ #pragma GCC diagnostic pop
342
+ #endif
343
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
344
+ }
345
+
346
+ /**
347
+ * @details
348
+ * Invokes the stored callable with the `Self_T` instance, surrounded by
349
+ * reporter calls to begin and end the suite.
350
+ */
351
+ void
352
+ suite::run (void)
353
+ {
354
+ #if defined(MICRO_OS_PLUS_TRACE) \
355
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
356
+ #if defined(__GNUC__)
357
+ #pragma GCC diagnostic push
358
+ #if defined(__clang__)
359
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
360
+ #endif
361
+ #endif
362
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
363
+ #if defined(__GNUC__)
364
+ #pragma GCC diagnostic pop
365
+ #endif
366
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
367
+
368
+ class reporter& reporter = this->reporter ();
369
+
370
+ this->timings ().timestamp_begin ();
371
+ reporter.begin_suite (*this);
372
+
373
+ // Invoke the callable, passing the self reference followed by the variadic
374
+ // arguments.
375
+ callable_ (*this);
376
+
377
+ this->timings ().timestamp_end ();
378
+ reporter.end_suite (*this);
379
+ }
380
+
381
+ // ==========================================================================
382
+
383
+ /**
384
+ * @details
385
+ * Initialises the base `suite` with @p name, the given @p runner, and
386
+ * a no-op callable. Sets `own_index` to 1, reserving index 0 for the
387
+ * runner itself. If tracing is enabled, the name is output for
388
+ * diagnostic purposes.
389
+ */
390
+ top_suite::top_suite (const char* name, class runner& runner)
391
+ : suite{ name, runner, [] (suite&) noexcept {} }
392
+ {
393
+ #if defined(MICRO_OS_PLUS_TRACE) \
394
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
395
+ #if defined(__GNUC__)
396
+ #pragma GCC diagnostic push
397
+ #if defined(__clang__)
398
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
399
+ #endif
400
+ #endif
401
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name);
402
+ #if defined(__GNUC__)
403
+ #pragma GCC diagnostic pop
404
+ #endif
405
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
406
+
407
+ own_index (1);
408
+ }
409
+
410
+ /**
411
+ * @details
412
+ * No resources are owned directly by `top_suite`; the destructor
413
+ * performs no explicit clean-up. If tracing is enabled, the suite
414
+ * name is output for diagnostic purposes.
415
+ */
416
+ top_suite::~top_suite ()
417
+ {
418
+ #if defined(MICRO_OS_PLUS_TRACE) \
419
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
420
+ #if defined(__GNUC__)
421
+ #pragma GCC diagnostic push
422
+ #if defined(__clang__)
423
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
424
+ #endif
425
+ #endif
426
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
427
+ #if defined(__GNUC__)
428
+ #pragma GCC diagnostic pop
429
+ #endif
430
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
431
+ }
432
+
433
+ // ==========================================================================
434
+
435
+ /**
436
+ * @details
437
+ * No resources are owned directly by `static_suite`; the destructor
438
+ * performs no explicit clean-up. If tracing is enabled, the suite
439
+ * name is output for diagnostic purposes.
440
+ */
441
+ static_suite::~static_suite ()
442
+ {
443
+ #if defined(MICRO_OS_PLUS_TRACE) \
444
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
445
+ #if defined(__GNUC__)
446
+ #pragma GCC diagnostic push
447
+ #if defined(__clang__)
448
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
449
+ #endif
450
+ #endif
451
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
452
+ #if defined(__GNUC__)
453
+ #pragma GCC diagnostic pop
454
+ #endif
455
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
456
+ }
457
+
458
+ /**
459
+ * @details
460
+ * Records the suite begin timestamp, notifies the reporter via
461
+ * `begin_suite()`, invokes the stored static callable with `*this`,
462
+ * records the suite end timestamp, and notifies the reporter via
463
+ * `end_suite()`.
464
+ */
465
+ void
466
+ static_suite::run (void)
467
+ {
468
+ #if defined(MICRO_OS_PLUS_TRACE) \
469
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
470
+ #if defined(__GNUC__)
471
+ #pragma GCC diagnostic push
472
+ #if defined(__clang__)
473
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
474
+ #endif
475
+ #endif
476
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
477
+ #if defined(__GNUC__)
478
+ #pragma GCC diagnostic pop
479
+ #endif
480
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
481
+
482
+ class reporter& reporter = this->reporter ();
483
+
484
+ this->timings ().timestamp_begin ();
485
+ reporter.begin_suite (*this);
486
+
487
+ static_callable_ (*this);
488
+
489
+ this->timings ().timestamp_end ();
490
+ reporter.end_suite (*this);
491
+ }
492
+
493
+ // --------------------------------------------------------------------------
494
+ } // namespace micro_os_plus::micro_test_plus
495
+
496
+ // ----------------------------------------------------------------------------
@@ -0,0 +1,209 @@
1
+ /*
2
+ * This file is part of the µOS++ project (https://micro-os-plus.github.io/).
3
+ * Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
4
+ *
5
+ * Permission to use, copy, modify, and/or distribute this software for any
6
+ * purpose is hereby granted, under the terms of the MIT license.
7
+ *
8
+ * If a copy of the license was not distributed with this file, it can be
9
+ * obtained from https://opensource.org/licenses/mit.
10
+ *
11
+ * Major parts of the code are inspired from v1.1.8 of the Boost UT project,
12
+ * released under the terms of the Boost Version 1.0 Software License,
13
+ * which can be obtained from https://www.boost.org/LICENSE_1_0.txt.
14
+ */
15
+
16
+ // ----------------------------------------------------------------------------
17
+
18
+ /**
19
+ * @file
20
+ * @brief C++ source file with implementations for the µTest++ timings
21
+ * methods.
22
+ *
23
+ * @details
24
+ * This source file contains the implementations for the `timestamp` and
25
+ * `timestamps` classes of the µTest++ framework. It provides methods
26
+ * for capturing monotonic clock values, checking whether valid
27
+ * timestamps have been recorded, and computing the elapsed time in
28
+ * milliseconds and microseconds between a begin and an end timestamp.
29
+ *
30
+ * All definitions reside within the
31
+ * `micro_os_plus::micro_test_plus` namespace.
32
+ *
33
+ * This file must be included when building the µTest++ library.
34
+ */
35
+
36
+ // ----------------------------------------------------------------------------
37
+
38
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
39
+ #include <micro-os-plus/config.h>
40
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
41
+
42
+ #include <micro-os-plus/micro-test-plus.h>
43
+
44
+ #include <cassert>
45
+
46
+ // ----------------------------------------------------------------------------
47
+
48
+ #if defined(__GNUC__)
49
+ #pragma GCC diagnostic ignored "-Waggregate-return"
50
+ #if defined(__clang__)
51
+ #pragma clang diagnostic ignored "-Wpre-c++17-compat"
52
+ #pragma clang diagnostic ignored "-Wc++98-compat"
53
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
54
+ #endif
55
+ #endif
56
+
57
+ // ============================================================================
58
+
59
+ namespace micro_os_plus::micro_test_plus
60
+ {
61
+ // --------------------------------------------------------------------------
62
+
63
+ /**
64
+ * @details
65
+ * On Windows, the current time is captured via `timespec_get()` with
66
+ * `TIME_UTC`. On POSIX platforms with `CLOCK_MONOTONIC` defined,
67
+ * `clock_gettime(CLOCK_MONOTONIC)` is used to obtain a monotonic
68
+ * timestamp. On platforms where neither macro is defined, `value_`
69
+ * remains zero-initialised.
70
+ */
71
+ timestamp::timestamp () noexcept
72
+ {
73
+ #if defined(_WIN32)
74
+ timespec_get (&value_, TIME_UTC);
75
+ #elif defined(CLOCK_MONOTONIC)
76
+ clock_gettime (CLOCK_MONOTONIC, &value_);
77
+ #endif
78
+ }
79
+
80
+ /**
81
+ * @details
82
+ * Returns `true` if at least one of the `tv_sec` or `tv_nsec` fields
83
+ * of the underlying `timespec` is non-zero, indicating that a valid
84
+ * clock reading was successfully captured.
85
+ */
86
+ bool
87
+ timestamp::has_clock (void) const noexcept
88
+ {
89
+ return value_.tv_sec != 0 || value_.tv_nsec != 0;
90
+ }
91
+
92
+ // --------------------------------------------------------------------------
93
+
94
+ /**
95
+ * @details
96
+ * If the begin timestamp has not yet been set, a `timestamp` is
97
+ * constructed in-place using the default constructor, which captures
98
+ * the current monotonic time. Subsequent calls are silently ignored,
99
+ * ensuring idempotent behaviour.
100
+ */
101
+ void
102
+ timestamps::timestamp_begin (void) noexcept
103
+ {
104
+ // Ensure it is timestamped only once.
105
+ if (!begin_time_.has_value ())
106
+ {
107
+ begin_time_.emplace ();
108
+ }
109
+ }
110
+
111
+ /**
112
+ * @details
113
+ * If the begin timestamp has not yet been set, a `timestamp` is
114
+ * constructed in-place from the supplied `timespec` value. Subsequent
115
+ * calls are silently ignored, ensuring idempotent behaviour.
116
+ */
117
+ void
118
+ timestamps::timestamp_begin (const timespec& ts) noexcept
119
+ {
120
+ // Ensure it is timestamped only once.
121
+ if (!begin_time_.has_value ())
122
+ {
123
+ begin_time_.emplace (ts);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * @details
129
+ * If the end timestamp has not yet been set, a `timestamp` is
130
+ * constructed in-place using the default constructor, which captures
131
+ * the current monotonic time. Subsequent calls are silently ignored,
132
+ * ensuring idempotent behaviour.
133
+ */
134
+ void
135
+ timestamps::timestamp_end (void) noexcept
136
+ {
137
+ // Ensure it is timestamped only once.
138
+ if (!end_time_.has_value ())
139
+ {
140
+ end_time_.emplace ();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * @details
146
+ * If the end timestamp has not yet been set, a `timestamp` is
147
+ * constructed in-place from the supplied `timespec` value. Subsequent
148
+ * calls are silently ignored, ensuring idempotent behaviour.
149
+ */
150
+ void
151
+ timestamps::timestamp_end (const timespec& ts) noexcept
152
+ {
153
+ // Ensure it is timestamped only once.
154
+ if (!end_time_.has_value ())
155
+ {
156
+ end_time_.emplace (ts);
157
+ }
158
+ }
159
+
160
+ /**
161
+ * @details
162
+ * Returns `true` only when both the begin and end optional timestamps
163
+ * are engaged and each contains a valid (non-zero) clock reading, as
164
+ * determined by `timestamp::has_clock()`.
165
+ */
166
+ bool
167
+ timestamps::has_timestamps (void) const noexcept
168
+ {
169
+ return begin_time_.has_value () && begin_time_->has_clock ()
170
+ && end_time_.has_value () && end_time_->has_clock ();
171
+ }
172
+
173
+ /**
174
+ * @details
175
+ * Subtracts the begin timestamp from the end timestamp in nanoseconds.
176
+ * If the nanosecond difference is negative, one second is borrowed
177
+ * from the seconds delta to normalise the result. The total elapsed
178
+ * duration in microseconds is then split into whole milliseconds
179
+ * (written to @p milliseconds) and the remainder microseconds (written
180
+ * to @p microseconds). Requires `has_timestamps()` to be `true`;
181
+ * behaviour is undefined otherwise.
182
+ */
183
+ void
184
+ timestamps::compute_elapsed_time (uint32_t& milliseconds,
185
+ uint32_t& microseconds) const
186
+ {
187
+ assert (has_timestamps ());
188
+
189
+ // Precondition: has_timestamps() must be true before calling this method.
190
+ // Invoking it with disengaged optionals is undefined behaviour.
191
+ long long delta_ns
192
+ = end_time_->value ().tv_nsec - begin_time_->value ().tv_nsec;
193
+ long long delta_s
194
+ = end_time_->value ().tv_sec - begin_time_->value ().tv_sec;
195
+ if (delta_ns < 0)
196
+ {
197
+ delta_ns += 1000000000LL;
198
+ --delta_s;
199
+ }
200
+
201
+ // Split into milliseconds and microseconds.
202
+ const long long total_us = delta_s * 1000000LL + delta_ns / 1000LL;
203
+ milliseconds = static_cast<uint32_t> (total_us / 1000LL);
204
+ microseconds = static_cast<uint32_t> (total_us % 1000LL);
205
+ }
206
+
207
+ // --------------------------------------------------------------------------
208
+
209
+ } // namespace micro_os_plus::micro_test_plus