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