@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/runner.cpp ADDED
@@ -0,0 +1,669 @@
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 runner
21
+ * methods.
22
+ *
23
+ * @details
24
+ * This source file contains the core implementations for the test runner
25
+ * facilities of the µTest++ framework. It provides the logic for initialising
26
+ * the test environment, registering and managing test suites, handling
27
+ * command-line arguments, orchestrating test execution, and determining the
28
+ * overall test result. The implementation supports automated discovery and
29
+ * execution of test suites, flexible verbosity control, and robust mechanisms
30
+ * for aborting test execution in critical scenarios.
31
+ *
32
+ * All definitions reside within the `micro_os_plus::micro_test_plus`
33
+ * namespace, ensuring clear separation from user code and minimising the risk
34
+ * of naming conflicts.
35
+ *
36
+ * This file must be included when building the µTest++ library.
37
+ */
38
+
39
+ // ----------------------------------------------------------------------------
40
+
41
+ #include <algorithm>
42
+ #include <string>
43
+
44
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
45
+ #include <micro-os-plus/config.h>
46
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
47
+
48
+ #if defined(MICRO_OS_PLUS_TRACE)
49
+ #include <micro-os-plus/diag/trace.h>
50
+ #endif // MICRO_OS_PLUS_TRACE
51
+
52
+ #include "micro-os-plus/micro-test-plus/runner.h"
53
+ #include "micro-os-plus/micro-test-plus/utility.h"
54
+ #include "micro-os-plus/micro-test-plus/reporter-tap.h"
55
+ #include "micro-os-plus/micro-test-plus/reporter-human.h"
56
+
57
+ // ----------------------------------------------------------------------------
58
+
59
+ #if defined(__GNUC__)
60
+ #pragma GCC diagnostic ignored "-Waggregate-return"
61
+ #if defined(__clang__)
62
+ #pragma clang diagnostic ignored "-Wc++98-compat"
63
+ #pragma clang diagnostic ignored "-Wc++98-c++11-c++14-compat"
64
+ #pragma clang diagnostic ignored "-Wpre-c++17-compat"
65
+ #endif
66
+ #endif
67
+
68
+ // ============================================================================
69
+
70
+ namespace micro_os_plus::micro_test_plus
71
+ {
72
+ // --------------------------------------------------------------------------
73
+
74
+ /**
75
+ * @details
76
+ * Performs the `static_runner` to `runner` upcast where both types are
77
+ * complete, allowing headers with only forward declarations to request this
78
+ * conversion safely.
79
+ */
80
+ runner&
81
+ detail::to_runner (static_runner& static_runner_ref) noexcept
82
+ {
83
+ return static_cast<runner&> (static_runner_ref);
84
+ }
85
+
86
+ /**
87
+ * @details
88
+ * Performs static-suite registration where `static_runner` is complete,
89
+ * allowing header-only template code to avoid direct dependence on
90
+ * `runner.h` include order.
91
+ */
92
+ void
93
+ detail::register_static_suite (static_runner& static_runner_ref,
94
+ static_suite& static_suite_ref)
95
+ {
96
+ static_runner::register_static_suite (static_runner_ref, static_suite_ref);
97
+ }
98
+
99
+ // --------------------------------------------------------------------------
100
+
101
+ /**
102
+ * @details
103
+ * The constructor initialises a new `runner` instance together with
104
+ * its top suite (`top_suite_`). If tracing is enabled, it outputs the
105
+ * function signature for diagnostic purposes.
106
+ */
107
+ runner::runner (void) : test_node{ "runner" }, top_suite_{ "", *this }
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
+ * @details
126
+ * The constructor initialises a new `runner` instance together with
127
+ * its top suite (`top_suite_`). If tracing is enabled, it outputs the
128
+ * function signature for diagnostic purposes.
129
+ */
130
+ runner::runner (const char* top_suite_name)
131
+ : test_node{ "runner" }, top_suite_{ top_suite_name, *this }
132
+ {
133
+ #if defined(MICRO_OS_PLUS_TRACE) \
134
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
135
+ #if defined(__GNUC__)
136
+ #pragma GCC diagnostic push
137
+ #if defined(__clang__)
138
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
139
+ #endif
140
+ #endif
141
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name ());
142
+ #if defined(__GNUC__)
143
+ #pragma GCC diagnostic pop
144
+ #endif
145
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
146
+ }
147
+
148
+ /**
149
+ * @details
150
+ * The `reporter_` unique pointer is released automatically. If tracing
151
+ * is enabled, the function signature is output for diagnostic purposes.
152
+ */
153
+ runner::~runner ()
154
+ {
155
+ #if defined(MICRO_OS_PLUS_TRACE) \
156
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
157
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
158
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
159
+
160
+ // reporter_ is a unique_ptr; destroyed automatically.
161
+ }
162
+
163
+ #if defined(__GNUC__)
164
+ #pragma GCC diagnostic push
165
+ #if defined(__clang__)
166
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
167
+ #endif
168
+ #endif
169
+ /**
170
+ * @details
171
+ * Captures command-line arguments, selects the reporter implementation
172
+ * (`human` or `tap`), starts session timing, and emits the initial
173
+ * reporter notifications for the session and top suite.
174
+ *
175
+ * If the top suite name is not provided, it attempts to derive a name from
176
+ * `argv[0]` or defaults to "default suite".
177
+ *
178
+ * If tracing is enabled, the command-line arguments are
179
+ * also logged for diagnostic purposes.
180
+ */
181
+ suite&
182
+ runner::initialise (int argc, char* argv[], const char* top_suite_name)
183
+ {
184
+ #if defined(MICRO_OS_PLUS_TRACE) \
185
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
186
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
187
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
188
+
189
+ #if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) && defined(MICRO_OS_PLUS_TRACE))
190
+ #if defined(MICRO_OS_PLUS_DEBUG)
191
+ trace::printf ("argv[");
192
+ for (int i = 0; i < argc; ++i)
193
+ {
194
+ if (i > 0)
195
+ {
196
+ trace::printf (", ");
197
+ }
198
+ trace::printf ("'%s'", argv[i]);
199
+ }
200
+ trace::puts ("]");
201
+ #endif // defined(MICRO_OS_PLUS_DEBUG)
202
+ #endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
203
+
204
+ if (strlen (top_suite_name) > 0)
205
+ {
206
+ // If provided by this call, use it, possibly override the
207
+ // deprecated constructor.
208
+ top_suite_name_ = top_suite_name;
209
+ top_suite_.name (top_suite_name_.c_str ());
210
+ }
211
+ else if (strlen (top_suite_.name ()) == 0)
212
+ {
213
+ // If not provided by the constructor or by this call, try to extract a
214
+ // name from argv[0], which is commonly the executable name. If that
215
+ // fails, use a default name.
216
+ if (argc > 0 && argv != nullptr && argv[0] != nullptr)
217
+ {
218
+ std::string_view top_suite_name_view{ utility::extract_file_name (
219
+ argv[0]) };
220
+
221
+ const auto dot_pos = top_suite_name_view.rfind ('.');
222
+ if (dot_pos != std::string_view::npos)
223
+ {
224
+ top_suite_name_view = top_suite_name_view.substr (0, dot_pos);
225
+ }
226
+
227
+ top_suite_name_ = top_suite_name_view;
228
+ }
229
+ else
230
+ {
231
+ top_suite_name_ = "default suite";
232
+ }
233
+ top_suite_.name (top_suite_name_.c_str ());
234
+ }
235
+
236
+ std::vector<std::string_view> argvs (argv, argv + argc);
237
+
238
+ std::string_view reporter_name{ "tap" };
239
+ static constexpr std::string_view reporter_prefix{ "--reporter=" };
240
+ for (size_t i = 0; i < argvs.size (); ++i)
241
+ {
242
+ if (argvs[i].starts_with (reporter_prefix))
243
+ {
244
+ reporter_name = argvs[i].substr (reporter_prefix.size ());
245
+ }
246
+ else if (argvs[i]
247
+ == reporter_prefix.substr (0, reporter_prefix.size () - 1))
248
+ {
249
+ if (i + 1 < argvs.size ())
250
+ {
251
+ reporter_name = argvs[++i];
252
+ }
253
+ else
254
+ {
255
+ fprintf (stderr, "error: --reporter option requires a "
256
+ "reporter name argument\n");
257
+ exit (1);
258
+ }
259
+ }
260
+ }
261
+
262
+ // Initialise and configure the reporter.
263
+ if (reporter_name == "human")
264
+ {
265
+ reporter_ = std::make_unique<reporter_human> (
266
+ std::make_unique<std::vector<std::string_view>> (
267
+ std::move (argvs)));
268
+ }
269
+ else if (reporter_name == "tap")
270
+ {
271
+ reporter_ = std::make_unique<reporter_tap> (
272
+ std::make_unique<std::vector<std::string_view>> (
273
+ std::move (argvs)));
274
+ }
275
+ else
276
+ {
277
+ fprintf (stderr, "error: unknown reporter '%.*s'\n",
278
+ static_cast<int> (reporter_name.size ()),
279
+ reporter_name.data ());
280
+ exit (1);
281
+ }
282
+
283
+ // ------------------------------------------------------------------------
284
+
285
+ timings_.timestamp_begin ();
286
+ reporter_->begin_session (*this);
287
+
288
+ top_suite_.timings ().timestamp_begin ();
289
+ reporter_->begin_suite (top_suite_);
290
+
291
+ return top_suite_;
292
+ }
293
+ #if defined(__GNUC__)
294
+ #pragma GCC diagnostic pop
295
+ #endif
296
+
297
+ // --------------------------------------------------------------------------
298
+
299
+ /**
300
+ * @details
301
+ * Takes ownership of the supplied `suite` unique pointer and appends
302
+ * it to the internal `children_suites_` vector, deferring execution
303
+ * until `run_suites_()` is called. If tracing is enabled, the suite
304
+ * name is logged for diagnostic purposes.
305
+ */
306
+ void
307
+ runner::register_suite_ (std::unique_ptr<class suite> suite)
308
+ {
309
+ #if defined(MICRO_OS_PLUS_TRACE) \
310
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
311
+ #if defined(__GNUC__)
312
+ #pragma GCC diagnostic push
313
+ #if defined(__clang__)
314
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
315
+ #endif
316
+ #endif
317
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite->name ());
318
+ #if defined(__GNUC__)
319
+ #pragma GCC diagnostic pop
320
+ #endif
321
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
322
+
323
+ children_suites_.push_back (std::move (suite));
324
+ }
325
+
326
+ /**
327
+ * @details
328
+ * Sorts `children_suites_` alphabetically by suite name using a
329
+ * selection sort on `unique_ptr::swap`, avoiding the
330
+ * `-Waggregate-return` diagnostic that `std::sort` would trigger on
331
+ * unique-pointer iterators. Each suite is assigned a 1-based index
332
+ * offset by the top-suite index, executed via `suite::run()`, and its
333
+ * totals are accumulated into the runner totals. The executed-subtest
334
+ * counter is not incremented; suites are not counted as subtests.
335
+ */
336
+ void
337
+ runner::run_suites_ (void)
338
+ {
339
+ // Use selection sort with unique_ptr::swap (returns void) to avoid
340
+ // std::sort triggering -Waggregate-return via std::move_backward,
341
+ // which returns a class-type iterator when operating on unique_ptr
342
+ // elements.
343
+ const size_t n = children_suites_.size ();
344
+ for (size_t i = 0; i < n; ++i)
345
+ {
346
+ size_t min_idx = i;
347
+ for (size_t j = i + 1; j < n; ++j)
348
+ {
349
+ if (std::string_view{ children_suites_[j]->name () }
350
+ < std::string_view{ children_suites_[min_idx]->name () })
351
+ min_idx = j;
352
+ }
353
+ if (min_idx != i)
354
+ children_suites_[i].swap (children_suites_[min_idx]);
355
+ }
356
+
357
+ for (size_t i = 0; i < n; ++i)
358
+ {
359
+ auto* suite_ptr = children_suites_[i].get ();
360
+
361
+ // +1 for 1-based index, +1 for top suite
362
+ suite_ptr->own_index (i + 1 + 1);
363
+
364
+ // Run the child suite immediately.
365
+ suite_ptr->run ();
366
+
367
+ // Accumulate the totals from the child suite into the runner
368
+ // totals.
369
+ // DO NOT increment executed_subtests here.
370
+ totals_ += suite_ptr->totals ();
371
+ }
372
+ }
373
+
374
+ /**
375
+ * @details
376
+ * Finalises the top suite by recording its end timestamp and notifying
377
+ * the reporter, then accumulates its totals into the runner totals.
378
+ * Subsequently invokes `run_suites_()` to sort, execute, and
379
+ * accumulate all registered child suites. Finally, records the session
380
+ * end timestamp, notifies the reporter, and returns `0` if all checks
381
+ * passed or `1` otherwise.
382
+ */
383
+ int
384
+ runner::exit_code (void)
385
+ {
386
+ #if defined(MICRO_OS_PLUS_TRACE) \
387
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
388
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
389
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
390
+
391
+ if (reporter_ == nullptr)
392
+ {
393
+ fprintf (stderr, "error: test runner not initialised\n");
394
+ return 1;
395
+ }
396
+
397
+ top_suite_.timings ().timestamp_end ();
398
+ reporter_->end_suite (top_suite_);
399
+ totals_ += top_suite_.totals ();
400
+
401
+ run_suites_ ();
402
+
403
+ timings_.timestamp_end ();
404
+ reporter_->end_session (*this);
405
+
406
+ const int result = totals_.was_successful () ? 0 : 1;
407
+
408
+ #if defined(MICRO_OS_PLUS_TRACE) \
409
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
410
+ #if defined(__GNUC__)
411
+ #pragma GCC diagnostic push
412
+ #if defined(__clang__)
413
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
414
+ #endif
415
+ #endif
416
+ trace::printf ("%s -> %d\n", __PRETTY_FUNCTION__, result);
417
+ #if defined(__GNUC__)
418
+ #pragma GCC diagnostic pop
419
+ #endif
420
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
421
+
422
+ return result;
423
+ }
424
+
425
+ /**
426
+ * @details
427
+ * Prints the source location of the fatal error to `stderr` and then
428
+ * terminates the process via `::abort()`.
429
+ */
430
+ void
431
+ runner::abort (const reflection::source_location& sl)
432
+ {
433
+ #if defined(MICRO_OS_PLUS_TRACE) \
434
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
435
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
436
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
437
+
438
+ #if defined(__GNUC__)
439
+ #pragma GCC diagnostic push
440
+ #if defined(__clang__)
441
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
442
+ #endif
443
+ #endif
444
+ fprintf (stderr, "\nerror: test execution aborted at %s:%u\n",
445
+ reflection::short_name (sl.file_name ()), sl.line ());
446
+ #if defined(__GNUC__)
447
+ #pragma GCC diagnostic pop
448
+ #endif
449
+
450
+ ::abort ();
451
+ }
452
+
453
+ /**
454
+ * @details
455
+ * Returns the number of registered child suites plus one, accounting
456
+ * for the top suite.
457
+ */
458
+ size_t
459
+ runner::suites_count (void) const noexcept
460
+ {
461
+ return children_suites_.size () + 1;
462
+ }
463
+
464
+ /**
465
+ * @details
466
+ * For the base `runner`, the total suite count equals `suites_count()`,
467
+ * as there are no additional static suites.
468
+ */
469
+ size_t
470
+ runner::total_suites_count (void) const noexcept
471
+ {
472
+ return suites_count ();
473
+ }
474
+
475
+ // ==========================================================================
476
+
477
+ /**
478
+ * @details
479
+ * Delegates construction to the `runner` base class.
480
+ * If tracing is enabled, the function signature
481
+ * and suite name are output for diagnostic purposes.
482
+ */
483
+ static_runner::static_runner (void) : runner{}
484
+ {
485
+ #if defined(MICRO_OS_PLUS_TRACE) \
486
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
487
+ #if defined(__GNUC__)
488
+ #pragma GCC diagnostic push
489
+ #if defined(__clang__)
490
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
491
+ #endif
492
+ #endif
493
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name ());
494
+ #if defined(__GNUC__)
495
+ #pragma GCC diagnostic pop
496
+ #endif
497
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
498
+ }
499
+
500
+ /**
501
+ * @details
502
+ * Delegates construction to the `runner` base class with the given
503
+ * @p top_suite_name. If tracing is enabled, the function signature
504
+ * and suite name are output for diagnostic purposes.
505
+ */
506
+ static_runner::static_runner (const char* top_suite_name)
507
+ : runner{ top_suite_name }
508
+ {
509
+ #if defined(MICRO_OS_PLUS_TRACE) \
510
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
511
+ #if defined(__GNUC__)
512
+ #pragma GCC diagnostic push
513
+ #if defined(__clang__)
514
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
515
+ #endif
516
+ #endif
517
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name ());
518
+ #if defined(__GNUC__)
519
+ #pragma GCC diagnostic pop
520
+ #endif
521
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
522
+ }
523
+
524
+ /**
525
+ * @details
526
+ * If `static_children_suites_` is non-null, the dynamically allocated
527
+ * vector of raw pointers is deleted and the pointer is reset to
528
+ * `nullptr`. The pointed-to `static_suite` objects are not deleted,
529
+ * as they are static storage-duration objects. If tracing is enabled,
530
+ * the function signature is output for diagnostic purposes.
531
+ */
532
+ static_runner::~static_runner ()
533
+ {
534
+ #if defined(MICRO_OS_PLUS_TRACE) \
535
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
536
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
537
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
538
+
539
+ if (static_children_suites_ != nullptr)
540
+ {
541
+ // The tests are static, so we do not delete them, but we need to
542
+ // delete the array of pointers.
543
+ delete static_children_suites_;
544
+ static_children_suites_ = nullptr;
545
+ }
546
+ }
547
+
548
+ /**
549
+ * @details
550
+ * Returns the number of elements in `static_children_suites_`, or
551
+ * zero if the vector has not been allocated yet.
552
+ */
553
+ size_t
554
+ static_runner::static_suites_count (void) const noexcept
555
+ {
556
+ return static_children_suites_ != nullptr
557
+ ? static_children_suites_->size ()
558
+ : 0;
559
+ }
560
+
561
+ /**
562
+ * @details
563
+ * Returns the combined count of dynamically registered suites (from
564
+ * the base `runner`) and statically registered suites.
565
+ */
566
+ size_t
567
+ static_runner::total_suites_count (void) const noexcept
568
+ {
569
+ return suites_count () + static_suites_count ();
570
+ }
571
+
572
+ /**
573
+ * @details
574
+ * First invokes `runner::run_suites_()` to execute all dynamically
575
+ * registered child suites. If `static_children_suites_` is non-null,
576
+ * its contents are sorted alphabetically by suite name using selection
577
+ * sort on raw pointers, each suite is assigned a 1-based index offset
578
+ * by the dynamic suite count, executed via `suite::run()`, and its
579
+ * totals are accumulated into the runner totals.
580
+ */
581
+ void
582
+ static_runner::run_suites_ (void)
583
+ {
584
+ #if defined(MICRO_OS_PLUS_TRACE) \
585
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
586
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
587
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
588
+
589
+ runner::run_suites_ ();
590
+
591
+ if (static_children_suites_ != nullptr)
592
+ {
593
+ // Use selection sort with std::swap on raw pointers (returns void)
594
+ // to avoid std::sort triggering -Waggregate-return via
595
+ // std::move_backward returning a class-type iterator.
596
+ const size_t n = static_children_suites_->size ();
597
+ auto& suites = *static_children_suites_;
598
+ for (size_t i = 0; i < n; ++i)
599
+ {
600
+ size_t min_idx = i;
601
+ for (size_t j = i + 1; j < n; ++j)
602
+ {
603
+ if (std::string_view{ suites[j]->name () }
604
+ < std::string_view{ suites[min_idx]->name () })
605
+ min_idx = j;
606
+ }
607
+ if (min_idx != i)
608
+ std::swap (suites[i], suites[min_idx]);
609
+ }
610
+
611
+ for (size_t i = 0; i < n; ++i)
612
+ {
613
+ auto* suite_ptr = suites[i];
614
+
615
+ suite_ptr->own_index (i + 1 + suites_count ());
616
+
617
+ // Run the child suite immediately.
618
+ suite_ptr->run ();
619
+
620
+ // Accumulate the totals from the static suite into the runner
621
+ // totals.
622
+ // DO NOT increment executed_subtests here.
623
+ totals_ += suite_ptr->totals ();
624
+ }
625
+ }
626
+ }
627
+
628
+ /**
629
+ * @details
630
+ * If `runner.static_children_suites_` is null, a new
631
+ * `std::vector<static_suite*>` is heap-allocated and assigned to it.
632
+ * The address of @p suite is then appended to the vector. This method
633
+ * is intended to be called from the constructor of `static_suite`,
634
+ * before test execution begins.
635
+ */
636
+ void
637
+ static_runner::register_static_suite (static_runner& runner,
638
+ static_suite& suite)
639
+ {
640
+ #if defined(MICRO_OS_PLUS_TRACE) \
641
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
642
+ #if defined(__GNUC__)
643
+ #pragma GCC diagnostic push
644
+ #if defined(__clang__)
645
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
646
+ #endif
647
+ #endif
648
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
649
+ #if defined(__GNUC__)
650
+ #pragma GCC diagnostic pop
651
+ #endif
652
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
653
+
654
+ if (runner.static_children_suites_ == nullptr)
655
+ {
656
+ #if defined(MICRO_OS_PLUS_TRACE) \
657
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
658
+ trace::printf ("%s new static_children_suites_ array\n",
659
+ __PRETTY_FUNCTION__);
660
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
661
+ runner.static_children_suites_ = new std::vector<static_suite*>;
662
+ }
663
+ runner.static_children_suites_->push_back (&suite);
664
+ }
665
+
666
+ // --------------------------------------------------------------------------
667
+ } // namespace micro_os_plus::micro_test_plus
668
+
669
+ // ----------------------------------------------------------------------------