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