@micro-os-plus/micro-test-plus 3.3.1 → 4.1.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 (65) hide show
  1. package/CHANGELOG.md +412 -2
  2. package/CMakeLists.txt +134 -28
  3. package/README.md +3 -2
  4. package/config/xcdl-build.json +11 -4
  5. package/include/micro-os-plus/micro-test-plus/README.md +6 -0
  6. package/include/micro-os-plus/micro-test-plus/deferred-reporter.h +267 -0
  7. package/include/micro-os-plus/micro-test-plus/detail.h +272 -1425
  8. package/include/micro-os-plus/micro-test-plus/exceptions.h +125 -0
  9. package/include/micro-os-plus/micro-test-plus/expression-formatter.h +669 -0
  10. package/include/micro-os-plus/micro-test-plus/function-comparators.h +15 -7
  11. package/include/micro-os-plus/micro-test-plus/inlines/{details-inlines.h → deferred-reporter-inlines.h} +66 -44
  12. package/include/micro-os-plus/micro-test-plus/inlines/detail-inlines.h +711 -0
  13. package/include/micro-os-plus/micro-test-plus/inlines/exceptions-inline.h +137 -0
  14. package/include/micro-os-plus/micro-test-plus/inlines/{test-reporter-inlines.h → expression-formatter-inlines.h} +232 -198
  15. package/include/micro-os-plus/micro-test-plus/inlines/function-comparators-inlines.h +24 -20
  16. package/include/micro-os-plus/micro-test-plus/inlines/literals-inlines.h +50 -31
  17. package/include/micro-os-plus/micro-test-plus/inlines/math-inlines.h +25 -19
  18. package/include/micro-os-plus/micro-test-plus/inlines/operators-inlines.h +275 -0
  19. package/include/micro-os-plus/micro-test-plus/inlines/reflection-inlines.h +39 -21
  20. package/include/micro-os-plus/micro-test-plus/inlines/reporter-inlines.h +205 -0
  21. package/include/micro-os-plus/micro-test-plus/inlines/runner-inlines.h +151 -0
  22. package/include/micro-os-plus/micro-test-plus/inlines/runner-totals-inlines.h +152 -0
  23. package/include/micro-os-plus/micro-test-plus/inlines/test-inlines.h +555 -0
  24. package/include/micro-os-plus/micro-test-plus/inlines/timings-inlines.h +120 -0
  25. package/include/micro-os-plus/micro-test-plus/inlines/type-traits-inlines.h +231 -0
  26. package/include/micro-os-plus/micro-test-plus/inlines/utility-inlines.h +126 -0
  27. package/include/micro-os-plus/micro-test-plus/literals.h +12 -17
  28. package/include/micro-os-plus/micro-test-plus/math.h +14 -6
  29. package/include/micro-os-plus/micro-test-plus/operators.h +53 -209
  30. package/include/micro-os-plus/micro-test-plus/reflection.h +8 -4
  31. package/include/micro-os-plus/micro-test-plus/{test-reporter-basic.h → reporter-human.h} +80 -74
  32. package/include/micro-os-plus/micro-test-plus/{test-reporter-tap.h → reporter-tap.h} +77 -71
  33. package/include/micro-os-plus/micro-test-plus/reporter.h +619 -0
  34. package/include/micro-os-plus/micro-test-plus/runner-totals.h +250 -0
  35. package/include/micro-os-plus/micro-test-plus/runner.h +472 -0
  36. package/include/micro-os-plus/micro-test-plus/test.h +1013 -0
  37. package/include/micro-os-plus/micro-test-plus/timings.h +363 -0
  38. package/include/micro-os-plus/micro-test-plus/type-traits.h +223 -577
  39. package/include/micro-os-plus/micro-test-plus/utility.h +136 -0
  40. package/include/micro-os-plus/micro-test-plus.h +42 -236
  41. package/meson.build +11 -6
  42. package/package.json +11 -3
  43. package/src/deferred-reporter.cpp +137 -0
  44. package/src/expression-formatter.cpp +289 -0
  45. package/src/reflection.cpp +97 -0
  46. package/src/reporter-human.cpp +816 -0
  47. package/src/reporter-tap.cpp +772 -0
  48. package/src/reporter.cpp +481 -0
  49. package/src/runner-totals.cpp +98 -0
  50. package/src/runner.cpp +669 -0
  51. package/src/test.cpp +503 -0
  52. package/src/timings.cpp +210 -0
  53. package/src/utility.cpp +163 -0
  54. package/.cmake-format.yaml +0 -11
  55. package/include/micro-os-plus/micro-test-plus/inlines/micro-test-plus-inlines.h +0 -313
  56. package/include/micro-os-plus/micro-test-plus/inlines/test-suite-inlines.h +0 -115
  57. package/include/micro-os-plus/micro-test-plus/test-reporter.h +0 -846
  58. package/include/micro-os-plus/micro-test-plus/test-runner.h +0 -281
  59. package/include/micro-os-plus/micro-test-plus/test-suite.h +0 -492
  60. package/src/micro-test-plus.cpp +0 -316
  61. package/src/test-reporter-basic.cpp +0 -466
  62. package/src/test-reporter-tap.cpp +0 -530
  63. package/src/test-reporter.cpp +0 -399
  64. package/src/test-runner.cpp +0 -311
  65. package/src/test-suite.cpp +0 -304
package/src/test.cpp ADDED
@@ -0,0 +1,503 @@
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
+ #if defined(MICRO_OS_PLUS_TRACE)
51
+ #include <micro-os-plus/diag/trace.h>
52
+ #endif // MICRO_OS_PLUS_TRACE
53
+
54
+ #include "micro-os-plus/micro-test-plus/test.h"
55
+ #include "micro-os-plus/micro-test-plus/runner.h"
56
+
57
+ // ----------------------------------------------------------------------------
58
+
59
+ #if defined(__GNUC__)
60
+ #if defined(__clang__)
61
+ #pragma clang diagnostic ignored "-Wc++98-compat"
62
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
63
+ #else // GCC only
64
+ #pragma GCC diagnostic ignored "-Wredundant-tags"
65
+ #pragma GCC diagnostic ignored "-Wsuggest-final-types"
66
+ #pragma GCC diagnostic ignored "-Wsuggest-final-methods"
67
+ #endif
68
+ #endif
69
+
70
+ // ============================================================================
71
+
72
+ namespace micro_os_plus::micro_test_plus
73
+ {
74
+ namespace detail
75
+ {
76
+ // ========================================================================
77
+
78
+ /**
79
+ * @details
80
+ * Stores the supplied @p name pointer, which is expected to point to
81
+ * a string with a lifetime exceeding that of this instance. If
82
+ * tracing is enabled, the name is output for diagnostic purposes.
83
+ */
84
+ test_node::test_node (const char* name) : name_{ name }
85
+ {
86
+ #if defined(MICRO_OS_PLUS_TRACE) \
87
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
88
+ #if defined(__GNUC__)
89
+ #pragma GCC diagnostic push
90
+ #if defined(__clang__)
91
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
92
+ #endif
93
+ #endif
94
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name);
95
+ #if defined(__GNUC__)
96
+ #pragma GCC diagnostic pop
97
+ #endif
98
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
99
+ }
100
+
101
+ /**
102
+ * @details
103
+ * No resources are owned by `test_node`; the destructor performs no
104
+ * explicit clean-up. If tracing is enabled, the node name is output
105
+ * for diagnostic purposes.
106
+ */
107
+ test_node::~test_node ()
108
+ {
109
+ #if defined(MICRO_OS_PLUS_TRACE) \
110
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
111
+ #if defined(__GNUC__)
112
+ #pragma GCC diagnostic push
113
+ #if defined(__clang__)
114
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
115
+ #endif
116
+ #endif
117
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
118
+ #if defined(__GNUC__)
119
+ #pragma GCC diagnostic pop
120
+ #endif
121
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
122
+ }
123
+
124
+ // ========================================================================
125
+
126
+ /**
127
+ * @details
128
+ * The constructor initialises a new instance of the `runnable_base` class
129
+ * with the specified name. It sets up the internal state required for
130
+ * managing test cases within the suite. If tracing is enabled, the
131
+ * function signature is output for diagnostic purposes. The default test
132
+ * suite does not require explicit registration, ensuring seamless
133
+ * integration within the µTest++ framework and supporting organised test
134
+ * management across all files and folders.
135
+ */
136
+ runnable_base::runnable_base (const char* name, class runner& runner,
137
+ size_t own_index)
138
+ : test_node{ name }, runner_{ runner }, own_index_{ own_index }
139
+ {
140
+ #if defined(MICRO_OS_PLUS_TRACE) \
141
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
142
+ #if defined(__GNUC__)
143
+ #pragma GCC diagnostic push
144
+ #if defined(__clang__)
145
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
146
+ #endif
147
+ #endif
148
+ trace::printf ("%s '%s' %zu\n", __PRETTY_FUNCTION__, name, own_index_);
149
+ #if defined(__GNUC__)
150
+ #pragma GCC diagnostic pop
151
+ #endif
152
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
153
+ }
154
+
155
+ /**
156
+ * @details
157
+ * The destructor releases any resources associated with the
158
+ * `runnable_base` instance. It ensures that the test suite is properly
159
+ * cleaned up after execution, supporting robust and reliable test
160
+ * management across all files and folders within the µTest++ framework.
161
+ */
162
+ runnable_base::~runnable_base ()
163
+ {
164
+ #if defined(MICRO_OS_PLUS_TRACE) \
165
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
166
+ #if defined(__GNUC__)
167
+ #pragma GCC diagnostic push
168
+ #if defined(__clang__)
169
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
170
+ #endif
171
+ #endif
172
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
173
+ #if defined(__GNUC__)
174
+ #pragma GCC diagnostic pop
175
+ #endif
176
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
177
+
178
+ // children_subtests_ holds unique_ptrs; destroyed automatically.
179
+ }
180
+
181
+ /**
182
+ * @details
183
+ * Delegates immediately to `runner_.reporter()`, returning the
184
+ * reporter associated with the owning runner instance.
185
+ */
186
+ [[nodiscard]] reporter&
187
+ runnable_base::reporter (void) const noexcept
188
+ {
189
+ return runner_.reporter ();
190
+ }
191
+
192
+ /**
193
+ * @details
194
+ * Delegates immediately to `runner_.abort()`, passing the supplied
195
+ * source location so that the error message identifies the call site
196
+ * before the process is terminated via `::abort()`.
197
+ */
198
+ [[noreturn]] void
199
+ runnable_base::abort (const reflection::source_location& sl)
200
+ {
201
+ runner_.abort (sl);
202
+ }
203
+
204
+ /**
205
+ * @details
206
+ * Transfers ownership of @p child_test into `children_subtests_` and
207
+ * immediately invokes `subtest::run()` on the newly stored subtest.
208
+ * The parent's executed-subtest counter is then incremented. The
209
+ * child's check counters are intentionally not merged into the parent
210
+ * totals; each subtest reports only its own counters. The child's
211
+ * totals are, however, accumulated into @p suite so that the suite
212
+ * summary reflects all checks performed by its subtests.
213
+ */
214
+ void
215
+ runnable_base::after_subtest_create_ (
216
+ std::unique_ptr<class subtest> child_test, suite& suite)
217
+ {
218
+ // Transfer ownership into the vector.
219
+ children_subtests_.push_back (std::move (child_test));
220
+
221
+ // Run the child test case immediately.
222
+ class subtest& subtest = *children_subtests_.back ();
223
+ subtest.run ();
224
+
225
+ // This test executed one more subtest.
226
+ #if defined(MICRO_OS_PLUS_TRACE) \
227
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
228
+ #if defined(__GNUC__)
229
+ #pragma GCC diagnostic push
230
+ #if defined(__clang__)
231
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
232
+ #endif
233
+ #endif
234
+ trace::printf ("%s subtest '%s' executed one more subtest\n",
235
+ __PRETTY_FUNCTION__, name ());
236
+ #if defined(__GNUC__)
237
+ #pragma GCC diagnostic pop
238
+ #endif
239
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
240
+ totals ().increment_executed_subtests ();
241
+ // Do not accumulate the totals from the child test into the current test
242
+ // totals, each subtest shows only its counters.
243
+
244
+ #if defined(MICRO_OS_PLUS_TRACE) \
245
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
246
+ #if defined(__GNUC__)
247
+ #pragma GCC diagnostic push
248
+ #if defined(__clang__)
249
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
250
+ #endif
251
+ #endif
252
+ trace::printf ("%s suite '%s' totals\n", __PRETTY_FUNCTION__,
253
+ suite.name ());
254
+ #if defined(__GNUC__)
255
+ #pragma GCC diagnostic pop
256
+ #endif
257
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
258
+
259
+ // Accumulate the totals from the child test into the suite totals.
260
+ suite.totals () += subtest.totals ();
261
+ }
262
+ } // namespace detail
263
+
264
+ // ==========================================================================
265
+
266
+ /**
267
+ * @details
268
+ * The destructor releases any resources associated with the
269
+ * `subtest` instance. If tracing is enabled, it outputs the
270
+ * function signature for diagnostic purposes. This ensures that the test
271
+ * suite is properly cleaned up after execution, supporting robust and
272
+ * reliable test management across all files and folders within the µTest++
273
+ * framework.
274
+ */
275
+ subtest::~subtest ()
276
+ {
277
+ #if defined(MICRO_OS_PLUS_TRACE) \
278
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
279
+ #if defined(__GNUC__)
280
+ #pragma GCC diagnostic push
281
+ #if defined(__clang__)
282
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
283
+ #endif
284
+ #endif
285
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
286
+ #if defined(__GNUC__)
287
+ #pragma GCC diagnostic pop
288
+ #endif
289
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
290
+ }
291
+
292
+ /**
293
+ * @details
294
+ * Invokes the stored callable with the `Self_T` instance, surrounded by
295
+ * reporter calls to begin and end the subtest.
296
+ */
297
+ void
298
+ subtest::run (void)
299
+ {
300
+ #if defined(MICRO_OS_PLUS_TRACE) \
301
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
302
+ #if defined(__GNUC__)
303
+ #pragma GCC diagnostic push
304
+ #if defined(__clang__)
305
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
306
+ #endif
307
+ #endif
308
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
309
+ #if defined(__GNUC__)
310
+ #pragma GCC diagnostic pop
311
+ #endif
312
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
313
+
314
+ class reporter& reporter = this->reporter ();
315
+
316
+ // For now, subtests do not record the time.
317
+ // this->timings.timestamp_begin ();
318
+ reporter.begin_subtest (*this);
319
+
320
+ // Invoke the callable, passing the self reference followed by the variadic
321
+ // arguments.
322
+ callable_ (*this);
323
+
324
+ // this->timings.timestamp_end ();
325
+ reporter.end_subtest (*this);
326
+ }
327
+
328
+ // ==========================================================================
329
+
330
+ /**
331
+ * @details
332
+ * No resources are owned directly by `suite`; the destructor performs
333
+ * no explicit clean-up. If tracing is enabled, the suite name is
334
+ * output for diagnostic purposes.
335
+ */
336
+ suite::~suite ()
337
+ {
338
+ #if defined(MICRO_OS_PLUS_TRACE) \
339
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
340
+ #if defined(__GNUC__)
341
+ #pragma GCC diagnostic push
342
+ #if defined(__clang__)
343
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
344
+ #endif
345
+ #endif
346
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
347
+ #if defined(__GNUC__)
348
+ #pragma GCC diagnostic pop
349
+ #endif
350
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
351
+ }
352
+
353
+ /**
354
+ * @details
355
+ * Invokes the stored callable with the `Self_T` instance, surrounded by
356
+ * reporter calls to begin and end the suite.
357
+ */
358
+ void
359
+ suite::run (void)
360
+ {
361
+ #if defined(MICRO_OS_PLUS_TRACE) \
362
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
363
+ #if defined(__GNUC__)
364
+ #pragma GCC diagnostic push
365
+ #if defined(__clang__)
366
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
367
+ #endif
368
+ #endif
369
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
370
+ #if defined(__GNUC__)
371
+ #pragma GCC diagnostic pop
372
+ #endif
373
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
374
+
375
+ class reporter& reporter = this->reporter ();
376
+
377
+ this->timings ().timestamp_begin ();
378
+ reporter.begin_suite (*this);
379
+
380
+ // Invoke the callable, passing the self reference followed by the variadic
381
+ // arguments.
382
+ callable_ (*this);
383
+
384
+ this->timings ().timestamp_end ();
385
+ reporter.end_suite (*this);
386
+ }
387
+
388
+ // ==========================================================================
389
+
390
+ /**
391
+ * @details
392
+ * Initialises the base `suite` with @p name, the given @p runner, and
393
+ * a no-op callable. Sets `own_index` to 1, reserving index 0 for the
394
+ * runner itself. If tracing is enabled, the name is output for
395
+ * diagnostic purposes.
396
+ */
397
+ top_suite::top_suite (const char* name, class runner& runner)
398
+ : suite{ name, runner, [] (suite&) noexcept {} }
399
+ {
400
+ #if defined(MICRO_OS_PLUS_TRACE) \
401
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
402
+ #if defined(__GNUC__)
403
+ #pragma GCC diagnostic push
404
+ #if defined(__clang__)
405
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
406
+ #endif
407
+ #endif
408
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name);
409
+ #if defined(__GNUC__)
410
+ #pragma GCC diagnostic pop
411
+ #endif
412
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
413
+
414
+ own_index (1);
415
+ }
416
+
417
+ /**
418
+ * @details
419
+ * No resources are owned directly by `top_suite`; the destructor
420
+ * performs no explicit clean-up. If tracing is enabled, the suite
421
+ * name is output for diagnostic purposes.
422
+ */
423
+ top_suite::~top_suite ()
424
+ {
425
+ #if defined(MICRO_OS_PLUS_TRACE) \
426
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
427
+ #if defined(__GNUC__)
428
+ #pragma GCC diagnostic push
429
+ #if defined(__clang__)
430
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
431
+ #endif
432
+ #endif
433
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
434
+ #if defined(__GNUC__)
435
+ #pragma GCC diagnostic pop
436
+ #endif
437
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
438
+ }
439
+
440
+ // ==========================================================================
441
+
442
+ /**
443
+ * @details
444
+ * No resources are owned directly by `static_suite`; the destructor
445
+ * performs no explicit clean-up. If tracing is enabled, the suite
446
+ * name is output for diagnostic purposes.
447
+ */
448
+ static_suite::~static_suite ()
449
+ {
450
+ #if defined(MICRO_OS_PLUS_TRACE) \
451
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
452
+ #if defined(__GNUC__)
453
+ #pragma GCC diagnostic push
454
+ #if defined(__clang__)
455
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
456
+ #endif
457
+ #endif
458
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
459
+ #if defined(__GNUC__)
460
+ #pragma GCC diagnostic pop
461
+ #endif
462
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
463
+ }
464
+
465
+ /**
466
+ * @details
467
+ * Records the suite begin timestamp, notifies the reporter via
468
+ * `begin_suite()`, invokes the stored static callable with `*this`,
469
+ * records the suite end timestamp, and notifies the reporter via
470
+ * `end_suite()`.
471
+ */
472
+ void
473
+ static_suite::run (void)
474
+ {
475
+ #if defined(MICRO_OS_PLUS_TRACE) \
476
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
477
+ #if defined(__GNUC__)
478
+ #pragma GCC diagnostic push
479
+ #if defined(__clang__)
480
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
481
+ #endif
482
+ #endif
483
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
484
+ #if defined(__GNUC__)
485
+ #pragma GCC diagnostic pop
486
+ #endif
487
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
488
+
489
+ class reporter& reporter = this->reporter ();
490
+
491
+ this->timings ().timestamp_begin ();
492
+ reporter.begin_suite (*this);
493
+
494
+ static_callable_ (*this);
495
+
496
+ this->timings ().timestamp_end ();
497
+ reporter.end_suite (*this);
498
+ }
499
+
500
+ // --------------------------------------------------------------------------
501
+ } // namespace micro_os_plus::micro_test_plus
502
+
503
+ // ----------------------------------------------------------------------------
@@ -0,0 +1,210 @@
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
+ #include <cassert>
39
+
40
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
41
+ #include <micro-os-plus/config.h>
42
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
43
+
44
+ #include "micro-os-plus/micro-test-plus/timings.h"
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::detail
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
+ } // namespace micro_os_plus::micro_test_plus::detail
209
+
210
+ // ----------------------------------------------------------------------------