@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
@@ -0,0 +1,782 @@
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++ TAP suite
21
+ * reporter methods.
22
+ *
23
+ * @details
24
+ * This source file contains the implementations for `reporter_tap`,
25
+ * a concrete implementation of the `reporter` abstract interface that
26
+ * formats suite results according to the Test Anything Protocol (TAP).
27
+ *
28
+ * All definitions reside within the `micro_os_plus::micro_test_plus`
29
+ * namespace, ensuring clear separation from user code and minimising the risk
30
+ * of naming conflicts.
31
+ *
32
+ * This file must be included when building the µTest++ library.
33
+ */
34
+
35
+ // ----------------------------------------------------------------------------
36
+
37
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
38
+ #include <micro-os-plus/config.h>
39
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
40
+
41
+ #include <micro-os-plus/micro-test-plus.h>
42
+ #include <micro-os-plus/diag/trace.h>
43
+
44
+ // For the PRIu32 macro used in snprintf() formatting of uint32_t values.
45
+ #include <cinttypes>
46
+
47
+ // ----------------------------------------------------------------------------
48
+
49
+ #if defined(__GNUC__)
50
+ #pragma GCC diagnostic ignored "-Waggregate-return"
51
+ #if defined(__clang__)
52
+ #pragma clang diagnostic ignored "-Wunknown-warning-option"
53
+ #pragma clang diagnostic ignored "-Wc++98-compat"
54
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
55
+ #endif
56
+ #endif
57
+
58
+ // =============================================================================
59
+
60
+ namespace micro_os_plus::micro_test_plus
61
+ {
62
+ // --------------------------------------------------------------------------
63
+
64
+ /**
65
+ * @details
66
+ * Delegates construction to the `reporter` base class with the supplied
67
+ * argument vector. If tracing is enabled, the function signature is
68
+ * output for diagnostic purposes.
69
+ */
70
+ reporter_tap::reporter_tap (
71
+ std::unique_ptr<std::vector<std::string_view>> argvs)
72
+ : reporter{ std::move (argvs) }
73
+ {
74
+ #if defined(MICRO_OS_PLUS_TRACE) \
75
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
76
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
77
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
78
+ }
79
+
80
+ /**
81
+ * @details
82
+ * No resources are owned directly by `reporter_tap`; the destructor
83
+ * performs no explicit clean-up. If tracing is enabled, the function
84
+ * signature is output for diagnostic purposes.
85
+ */
86
+ reporter_tap::~reporter_tap ()
87
+ {
88
+ #if defined(MICRO_OS_PLUS_TRACE) \
89
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
90
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
91
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
92
+ }
93
+
94
+ // --------------------------------------------------------------------------
95
+
96
+ constexpr size_t indent_size = 4;
97
+
98
+ /**
99
+ * @details
100
+ * This operator overload appends spaces to the internal output buffer
101
+ * corresponding to `m.level` four-space indentation levels. It enables
102
+ * structured, readable nesting of suite output across all test cases and
103
+ * folders by allowing `*this << indent(n) << "text"` chaining.
104
+ */
105
+ reporter_tap&
106
+ reporter_tap::operator<< (indent_t m)
107
+ {
108
+ buffer_.append (m.level * indent_size, ' ');
109
+ return *this;
110
+ }
111
+
112
+ // --------------------------------------------------------------------------
113
+
114
+ /**
115
+ * @details
116
+ * If verbosity is not silent, emits a blank line to `stdout` and then
117
+ * calls `write_info_()` to output build and environment information.
118
+ * The TAP version header `"TAP version 14"` is then written to both
119
+ * the output file (if open) and `stdout`. The `add_empty_line_` flag
120
+ * is cleared so that the first suite header is not separated by an
121
+ * extra blank line.
122
+ */
123
+ void
124
+ reporter_tap::begin_session ([[maybe_unused]] runner& runner)
125
+ {
126
+ #if defined(MICRO_OS_PLUS_TRACE) \
127
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
128
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
129
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
130
+
131
+ #if defined(__GNUC__)
132
+ #pragma GCC diagnostic push
133
+ #if defined(__clang__)
134
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
135
+ #endif
136
+ #endif
137
+
138
+ if (verbosity_ != verbosity::silent)
139
+ {
140
+ printf ("\n");
141
+ }
142
+
143
+ write_info_ ();
144
+
145
+ const char* message = "TAP version 14";
146
+ if (output_file_ != nullptr)
147
+ {
148
+ fprintf (output_file_, "%s\n", message);
149
+ }
150
+
151
+ if (verbosity_ != verbosity::silent)
152
+ {
153
+ printf ("%s\n", message);
154
+
155
+ flush ();
156
+ }
157
+
158
+ add_empty_line_ = false;
159
+
160
+ #if defined(__GNUC__)
161
+ #pragma GCC diagnostic pop
162
+ #endif
163
+ }
164
+
165
+ /**
166
+ * @details
167
+ * Emits the TAP plan line (`1..N`, where N is the total number of test
168
+ * suites) followed by a comment line summarising successful checks,
169
+ * failed checks, executed test cases, elapsed time, and suite count.
170
+ * Both lines are written to the output file when one is open. When
171
+ * verbosity is set to quiet, the plan is replaced by `1..0` to
172
+ * produce a valid skipped-test TAP document.
173
+ */
174
+ void
175
+ reporter_tap::end_session (runner& runner)
176
+ {
177
+ #if defined(MICRO_OS_PLUS_TRACE) \
178
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
179
+ trace::printf ("%s\n", __PRETTY_FUNCTION__);
180
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
181
+
182
+ #if defined(__GNUC__)
183
+ #pragma GCC diagnostic push
184
+ #if defined(__clang__)
185
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
186
+ #endif
187
+ #endif
188
+
189
+ size_t total_suites_count = runner.total_suites_count ();
190
+
191
+ uint32_t milliseconds = 0;
192
+ uint32_t microseconds = 0;
193
+ if (runner.timings ().has_timestamps ())
194
+ {
195
+ runner.timings ().compute_elapsed_time (milliseconds, microseconds);
196
+ }
197
+
198
+ char message_summary[32];
199
+ snprintf (message_summary, sizeof (message_summary), "1..%zu",
200
+ total_suites_count);
201
+
202
+ char message_totals[160];
203
+ snprintf (message_totals, sizeof (message_totals),
204
+ "total: %zu check%s passed, %zu failed, in %zu test "
205
+ "case%s, %zu test suite%s",
206
+ runner.totals ().successful_checks (),
207
+ runner.totals ().successful_checks () == 1 ? "" : "s",
208
+ runner.totals ().failed_checks (),
209
+ runner.totals ().executed_subtests (),
210
+ runner.totals ().executed_subtests () == 1 ? "" : "s",
211
+ total_suites_count, total_suites_count == 1 ? "" : "s");
212
+
213
+ char message_time[120] = "";
214
+ if (milliseconds > 0 || microseconds > 0)
215
+ {
216
+ snprintf (message_time, sizeof (message_time),
217
+ ", time: %" PRIu32 ".%03" PRIu32 " ms", milliseconds,
218
+ microseconds);
219
+ }
220
+
221
+ if (output_file_ != nullptr)
222
+ {
223
+ fprintf (output_file_, "%s\n# { %s%s }\n", message_summary,
224
+ message_totals, message_time);
225
+ }
226
+
227
+ if (verbosity_ != verbosity::silent)
228
+ {
229
+ if (add_empty_line_)
230
+ {
231
+ printf ("\n");
232
+ }
233
+
234
+ if (verbosity_ != verbosity::quiet)
235
+ {
236
+ printf ("%s\n", message_summary);
237
+ }
238
+ else
239
+ {
240
+ // With quiet verbosity, there are no ok/not ok lines, so the TAP
241
+ // plan should look like a skipped test.
242
+ printf ("1..0\n");
243
+ }
244
+
245
+ printf ("# { %s%s }\n", message_totals, message_time);
246
+
247
+ flush ();
248
+ }
249
+
250
+ #if defined(__GNUC__)
251
+ #pragma GCC diagnostic pop
252
+ #endif
253
+ }
254
+
255
+ // --------------------------------------------------------------------------
256
+
257
+ /**
258
+ * @details
259
+ * Emits a `"# Subtest: <name>"` comment line to mark the start of a
260
+ * TAP subtest block for the suite. The line is written to the output
261
+ * file when one is open. Under normal or verbose verbosity, it is also
262
+ * written to `stdout`, preceded by a blank line when `add_empty_line_`
263
+ * is set.
264
+ */
265
+ void
266
+ reporter_tap::begin_suite (suite& suite)
267
+ {
268
+ #if defined(MICRO_OS_PLUS_TRACE) \
269
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
270
+ #pragma GCC diagnostic push
271
+ #if defined(__clang__)
272
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
273
+ #endif
274
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
275
+ #pragma GCC diagnostic pop
276
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
277
+
278
+ #if defined(__GNUC__)
279
+ #pragma GCC diagnostic push
280
+ #if defined(__clang__)
281
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
282
+ #endif
283
+ #endif
284
+
285
+ char message_subtest[120];
286
+ snprintf (message_subtest, sizeof (message_subtest), "# Subtest: %s",
287
+ suite.name ());
288
+
289
+ if (output_file_ != nullptr)
290
+ {
291
+ fprintf (output_file_, "%s\n", message_subtest);
292
+ }
293
+
294
+ if (verbosity_ == verbosity::normal || verbosity_ == verbosity::verbose)
295
+ {
296
+ if (add_empty_line_)
297
+ {
298
+ printf ("\n");
299
+ }
300
+
301
+ printf ("%s\n", message_subtest);
302
+
303
+ flush ();
304
+
305
+ add_empty_line_ = true;
306
+ }
307
+
308
+ #if defined(__GNUC__)
309
+ #pragma GCC diagnostic pop
310
+ #endif
311
+ }
312
+
313
+ /**
314
+ * @details
315
+ * Emits the TAP plan line for the suite's subtests followed by an
316
+ * `ok`/`not ok` result line that includes the suite index, name,
317
+ * pass/fail status, check counts, subtest count, and elapsed time.
318
+ * Any output accumulated in `buffer_` is flushed to the output file
319
+ * and, when verbosity is verbose or the suite failed, to `stdout`
320
+ * as well. The buffer is cleared on exit.
321
+ */
322
+ void
323
+ reporter_tap::end_suite (suite& suite)
324
+ {
325
+ #if defined(MICRO_OS_PLUS_TRACE) \
326
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
327
+ #pragma GCC diagnostic push
328
+ #if defined(__clang__)
329
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
330
+ #endif
331
+ trace::printf (
332
+ "%s '%s' +%zu -%zu in xc%zu, xs%zu | cs%zu\n", __PRETTY_FUNCTION__,
333
+ suite.name (), suite.totals ().successful_checks (),
334
+ suite.totals ().failed_checks (), suite.totals ().executed_checks (),
335
+ suite.totals ().executed_subtests (),
336
+ suite.children_subtests_count ());
337
+
338
+ #pragma GCC diagnostic pop
339
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
340
+
341
+ #if defined(__GNUC__)
342
+ #pragma GCC diagnostic push
343
+ #if defined(__clang__)
344
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
345
+ #endif
346
+ #endif
347
+
348
+ uint32_t milliseconds = 0;
349
+ uint32_t microseconds = 0;
350
+ if (suite.timings ().has_timestamps ())
351
+ {
352
+ suite.timings ().compute_elapsed_time (milliseconds, microseconds);
353
+ }
354
+
355
+ std::string indent (indent_size, ' ');
356
+
357
+ char message_summary[40];
358
+ snprintf (message_summary, sizeof (message_summary), "%s1..%zu",
359
+ indent.c_str (), suite.children_subtests_count ());
360
+ char message_totals[120];
361
+ if (suite.totals ().was_successful ()) [[likely]]
362
+ {
363
+ snprintf (message_totals, sizeof (message_totals),
364
+ "ok %zu - %s # { passed, %zu check%s in %zu "
365
+ "test case%s",
366
+ suite.own_index (), suite.name (),
367
+ suite.totals ().successful_checks (),
368
+ suite.totals ().successful_checks () == 1 ? "" : "s",
369
+ suite.totals ().executed_subtests (),
370
+ suite.totals ().executed_subtests () == 1 ? "" : "s");
371
+ }
372
+ else
373
+ {
374
+ snprintf (message_totals, sizeof (message_totals),
375
+ "not ok %zu - %s # { FAILED, %zu check%s "
376
+ "passed, %zu failed, in %zu test case%s",
377
+ suite.own_index (), suite.name (),
378
+ suite.totals ().successful_checks (),
379
+ suite.totals ().successful_checks () == 1 ? "" : "s",
380
+ suite.totals ().failed_checks (),
381
+ suite.totals ().executed_subtests (),
382
+ suite.totals ().executed_subtests () == 1 ? "" : "s");
383
+ }
384
+
385
+ char message_time[120] = "";
386
+ if (milliseconds > 0 || microseconds > 0)
387
+ {
388
+ snprintf (message_time, sizeof (message_time),
389
+ ", time: %" PRIu32 ".%03" PRIu32 " ms", milliseconds,
390
+ microseconds);
391
+ }
392
+
393
+ if (output_file_ != nullptr)
394
+ {
395
+ write_buffer_to_file_ ();
396
+
397
+ fprintf (output_file_, "%s\n%s%s }\n", message_summary, message_totals,
398
+ message_time);
399
+ }
400
+
401
+ // At this point, the buffer may contain output from the test case, which
402
+ // should be displayed.
403
+ if (verbosity_ == verbosity::normal || verbosity_ == verbosity::verbose)
404
+ {
405
+ if (add_empty_line_ && suite.totals ().executed_subtests () > 0)
406
+ {
407
+ printf ("\n");
408
+ }
409
+
410
+ if (suite.totals ().was_successful ()) [[likely]]
411
+ {
412
+ // Successful test suite.
413
+
414
+ if (verbosity_ == verbosity::verbose)
415
+ {
416
+ // With verbosity, show full TAP output accumulated in the
417
+ // buffer.
418
+ write_buffer_to_stdout ();
419
+ }
420
+
421
+ printf ("%s\n%s%s }\n", message_summary, message_totals,
422
+ message_time);
423
+ }
424
+ else
425
+ {
426
+ // Failed test suite.
427
+
428
+ // Show full TAP output accumulated in the buffer for failed suite
429
+ // cases, as it may contain useful information about the failure.
430
+ write_buffer_to_stdout ();
431
+
432
+ printf ("%s\n%s%s }\n", message_summary, message_totals,
433
+ message_time);
434
+ }
435
+
436
+ flush ();
437
+ }
438
+
439
+ buffer_.clear ();
440
+
441
+ add_empty_line_ = true;
442
+
443
+ #if defined(__GNUC__)
444
+ #pragma GCC diagnostic pop
445
+ #endif
446
+ }
447
+
448
+ // --------------------------------------------------------------------------
449
+
450
+ /**
451
+ * @details
452
+ * Validates that the output buffer is empty at subtest start; if not,
453
+ * the buffer is written to `stdout`, the stream is flushed, and
454
+ * execution is aborted. A `"# Subtest: <name>"` comment line
455
+ * (indented according to nesting depth) is written to the output file
456
+ * and, under normal or verbose verbosity, to `stdout` as well.
457
+ */
458
+ void
459
+ reporter_tap::begin_subtest (subtest& subtest)
460
+ {
461
+ #if defined(MICRO_OS_PLUS_TRACE) \
462
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
463
+ #pragma GCC diagnostic push
464
+ #if defined(__clang__)
465
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
466
+ #endif
467
+ trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, subtest.name ());
468
+ #pragma GCC diagnostic pop
469
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
470
+
471
+ #if defined(__GNUC__)
472
+ #pragma GCC diagnostic push
473
+ #if defined(__clang__)
474
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
475
+ #endif
476
+ #endif
477
+
478
+ if (!buffer_.empty ())
479
+ {
480
+ // Each suite should start with an empty buffer.
481
+ write_buffer_to_stdout ();
482
+ flush ();
483
+ abort ();
484
+ }
485
+
486
+ std::string indent (indent_size * subtest.nesting_depth (), ' ');
487
+
488
+ char message_subtest[120];
489
+ snprintf (message_subtest, sizeof (message_subtest), "%s# Subtest: %s",
490
+ indent.c_str (), subtest.name ());
491
+
492
+ if (output_file_ != nullptr)
493
+ {
494
+ fprintf (output_file_, "%s\n", message_subtest);
495
+ }
496
+
497
+ if (verbosity_ == verbosity::normal || verbosity_ == verbosity::verbose)
498
+ {
499
+ if (add_empty_line_)
500
+ {
501
+ printf ("\n");
502
+ }
503
+
504
+ printf ("%s\n", message_subtest);
505
+
506
+ flush ();
507
+
508
+ add_empty_line_ = false;
509
+ }
510
+
511
+ #if defined(__GNUC__)
512
+ #pragma GCC diagnostic pop
513
+ #endif
514
+ }
515
+
516
+ /**
517
+ * @details
518
+ * Emits the TAP plan line for the subtest's checks followed by an
519
+ * `ok`/`not ok` result line that includes the subtest index, name,
520
+ * pass/fail status, and check counts. Any output accumulated in
521
+ * `buffer_` is flushed to the output file and, when verbosity is
522
+ * verbose or the subtest failed, to `stdout` as well. The buffer is
523
+ * cleared on exit.
524
+ */
525
+ void
526
+ reporter_tap::end_subtest (subtest& subtest)
527
+ {
528
+ #if defined(MICRO_OS_PLUS_TRACE) \
529
+ && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
530
+ #pragma GCC diagnostic push
531
+ #if defined(__clang__)
532
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
533
+ #endif
534
+ trace::printf ("%s '%s' i%zu +%zu -%zu in xc%zu, xs%zu | cs%zu\n",
535
+ __PRETTY_FUNCTION__, subtest.name (),
536
+ subtest.nesting_depth (),
537
+ subtest.totals ().successful_checks (),
538
+ subtest.totals ().failed_checks (),
539
+ subtest.totals ().executed_checks (),
540
+ subtest.totals ().executed_subtests (),
541
+ subtest.children_subtests_count ());
542
+ #pragma GCC diagnostic pop
543
+ #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
544
+
545
+ #if defined(__GNUC__)
546
+ #pragma GCC diagnostic push
547
+ #if defined(__clang__)
548
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
549
+ #endif
550
+ #endif
551
+
552
+ std::string indent (indent_size * subtest.nesting_depth (), ' ');
553
+ std::string indent2 (indent_size * (subtest.nesting_depth () + 1), ' ');
554
+
555
+ char message_summary[40];
556
+ snprintf (message_summary, sizeof (message_summary), "%s1..%zu",
557
+ indent2.c_str (),
558
+ subtest.totals ().executed_checks ()
559
+ + subtest.totals ().executed_subtests ());
560
+
561
+ char message_totals[120];
562
+ if (subtest.totals ().was_successful ()) [[likely]]
563
+ {
564
+ snprintf (message_totals, sizeof (message_totals),
565
+ "%sok %zu - %s # { passed, %zu check%s }", indent.c_str (),
566
+ subtest.own_index (), subtest.name (),
567
+ subtest.totals ().successful_checks (),
568
+ subtest.totals ().successful_checks () == 1 ? "" : "s");
569
+ }
570
+ else
571
+ {
572
+ snprintf (message_totals, sizeof (message_totals),
573
+ "%snot ok %zu - %s # { FAILED, %zu check%s "
574
+ "passed, %zu failed }",
575
+ indent.c_str (), subtest.own_index (), subtest.name (),
576
+ subtest.totals ().successful_checks (),
577
+ subtest.totals ().successful_checks () == 1 ? "" : "s",
578
+ subtest.totals ().failed_checks ());
579
+ }
580
+
581
+ if (output_file_ != nullptr)
582
+ {
583
+ write_buffer_to_file_ ();
584
+
585
+ fprintf (output_file_, "%s\n%s\n", message_summary, message_totals);
586
+ }
587
+
588
+ // At this point, the buffer may contain output from the subtest, which
589
+ // should be displayed.
590
+ if (verbosity_ == verbosity::normal || verbosity_ == verbosity::verbose)
591
+ {
592
+ if (add_empty_line_)
593
+ {
594
+ printf ("\n");
595
+ }
596
+
597
+ if (subtest.totals ().was_successful ()) [[likely]]
598
+ {
599
+ // Successful subtest.
600
+ if (verbosity_ == verbosity::verbose)
601
+ {
602
+ // With verbosity, show full TAP output accumulated in the
603
+ // buffer.
604
+ write_buffer_to_stdout ();
605
+
606
+ printf ("%s\n", message_summary);
607
+ }
608
+ else
609
+ {
610
+ // Without verbosity, show only the summary line
611
+ // and count only subtests, not checks, as the TAP output is
612
+ // not shown.
613
+ printf ("%s1..%zu\n", indent2.c_str (),
614
+ subtest.totals ().executed_subtests ());
615
+ }
616
+
617
+ printf ("%s\n", message_totals);
618
+ }
619
+ else
620
+ {
621
+ // Failed subtest.
622
+
623
+ // Show full TAP output accumulated in the buffer for failed
624
+ // subtests, as it may contain useful information about the
625
+ // failure.
626
+ write_buffer_to_stdout ();
627
+
628
+ printf ("%s\n%s\n", message_summary, message_totals);
629
+ }
630
+
631
+ flush ();
632
+ }
633
+
634
+ buffer_.clear ();
635
+
636
+ add_empty_line_ = true;
637
+
638
+ #if defined(__GNUC__)
639
+ #pragma GCC diagnostic pop
640
+ #endif
641
+ }
642
+
643
+ // --------------------------------------------------------------------------
644
+
645
+ /**
646
+ * @details
647
+ * Returns `"# "`. The TAP reporter prefixes all comment lines,
648
+ * including the informational lines emitted by `write_info_()`, with
649
+ * the TAP comment marker.
650
+ */
651
+ const char*
652
+ reporter_tap::get_comment_prefix (void)
653
+ {
654
+ return "# ";
655
+ }
656
+
657
+ /**
658
+ * @details
659
+ * This method outputs the prefix for a passing suite result, applying the
660
+ * appropriate colour formatting and symbols to clearly indicate success. If
661
+ * the output occurs within a test case, additional indentation is applied
662
+ * for readability. The prefix includes a tick symbol (`✓`) and, if provided,
663
+ * an associated message. Colour formatting is reset after the prefix to
664
+ * maintain consistent output style across all test cases and folders.
665
+ *
666
+ * The prefix/suffix methods help shorten the code
667
+ * generated by the template methods.
668
+ */
669
+ void
670
+ reporter_tap::output_pass_prefix_ (std::string& message, subtest& subtest)
671
+ {
672
+ size_t level = subtest.nesting_depth ();
673
+ *this << indent (level + 1) << "ok "
674
+ << static_cast<int> (subtest.current_subtest_index ()) << " - ";
675
+ if (!message.empty ())
676
+ {
677
+ *this << message.c_str ();
678
+ }
679
+ }
680
+
681
+ /**
682
+ * @details
683
+ * The `endl` function acts as a stream manipulator for the `reporter`,
684
+ * inserting a line ending into the output buffer and flushing the current
685
+ * content if necessary. This ensures that suite report output is clearly
686
+ * separated and formatted, improving readability and professionalism in the
687
+ * presentation of suite results.
688
+ *
689
+ * Using `endl` in conjunction with the `reporter` output operators
690
+ * provides a familiar and convenient mechanism for managing line breaks,
691
+ * similar to standard C++ stream manipulators.
692
+ *
693
+ * The prefix/suffix methods help shorten the code
694
+ * generated by the template methods.
695
+ */
696
+ void
697
+ reporter_tap::output_pass_suffix_ ([[maybe_unused]] subtest& subtest)
698
+ {
699
+ *this << endl;
700
+
701
+ flush ();
702
+ }
703
+
704
+ /**
705
+ * @details
706
+ * This method outputs the prefix for a failing suite result, applying the
707
+ * appropriate colour formatting and symbols to clearly indicate failure. If
708
+ * the output occurs within a test case, additional indentation is applied
709
+ * for readability. The prefix includes a cross symbol (`✗`), an optional
710
+ * message, and the label "FAILED". The source location is appended in
711
+ * parentheses, showing the file or folder name and line number where the
712
+ * failure occurred. Colour formatting is reset after the prefix to maintain
713
+ * consistent output style across all test cases and folders.
714
+ */
715
+ void
716
+ reporter_tap::output_fail_prefix_ (
717
+ std::string& message, [[maybe_unused]] const bool hasExpression,
718
+ [[maybe_unused]] const reflection::source_location& location,
719
+ subtest& subtest)
720
+ {
721
+ size_t level = subtest.nesting_depth ();
722
+ *this << indent (level + 1) << "not ok "
723
+ << static_cast<int> (subtest.current_subtest_index ());
724
+
725
+ if (!message.empty ())
726
+ {
727
+ *this << " - " << message.c_str ();
728
+ }
729
+ *this << endl;
730
+
731
+ // https://testanything.org/tap-version-14-specification.html
732
+ // 2-space indentation for YAML diagnostics.
733
+ *this << indent (level + 1) << " ---";
734
+ if (hasExpression)
735
+ {
736
+ *this << endl;
737
+ *this << indent (level + 1) << " condition: ";
738
+ }
739
+ }
740
+
741
+ /**
742
+ * @details
743
+ * This method outputs the suffix for a failing suite result by closing the
744
+ * location information, appending an "aborted..." message if the suite was
745
+ * aborted, and then adding a newline to the suite output. The output stream
746
+ * is flushed to ensure immediate visibility. This approach guarantees that
747
+ * failure results are clearly separated, promptly reported, and easily
748
+ * distinguishable across all test cases and folders.
749
+ */
750
+ void
751
+ reporter_tap::output_fail_suffix_ (
752
+ const reflection::source_location& location, bool abort,
753
+ [[maybe_unused]] subtest& subtest)
754
+ {
755
+ size_t level = subtest.nesting_depth ();
756
+ if (abort)
757
+ {
758
+ *this << " aborted...";
759
+ }
760
+ *this << endl;
761
+
762
+ // https://testanything.org/tap-version-14-specification.html
763
+ // 2-space indentation for YAML diagnostics.
764
+
765
+ *this << indent (level + 1) << " at:" << endl;
766
+ *this << indent (level + 1)
767
+ << " filename: " << reflection::short_name (location.file_name ())
768
+ << endl;
769
+ *this << indent (level + 1) << " line: "
770
+ << type_traits::genuine_integral_value<unsigned int>{ location
771
+ .line () }
772
+ << endl;
773
+
774
+ *this << indent (level + 1) << " ..." << endl;
775
+
776
+ flush ();
777
+ }
778
+
779
+ // --------------------------------------------------------------------------
780
+ } // namespace micro_os_plus::micro_test_plus
781
+
782
+ // ----------------------------------------------------------------------------