@micro-os-plus/micro-test-plus 3.3.1 → 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.
- package/CHANGELOG.md +330 -2
- package/CMakeLists.txt +79 -23
- package/README.md +1 -1
- package/config/xcdl-build.json +11 -4
- package/include/micro-os-plus/micro-test-plus/deferred-reporter.h +292 -0
- package/include/micro-os-plus/micro-test-plus/detail.h +462 -1076
- package/include/micro-os-plus/micro-test-plus/exceptions.h +126 -0
- package/include/micro-os-plus/micro-test-plus/function-comparators.h +10 -7
- package/include/micro-os-plus/micro-test-plus/inlines/{details-inlines.h → deferred-reporter-inlines.h} +49 -22
- package/include/micro-os-plus/micro-test-plus/inlines/function-comparators-inlines.h +67 -4
- package/include/micro-os-plus/micro-test-plus/inlines/literals-inlines.h +3 -6
- package/include/micro-os-plus/micro-test-plus/inlines/math-inlines.h +21 -15
- package/include/micro-os-plus/micro-test-plus/inlines/reflection-inlines.h +35 -17
- package/include/micro-os-plus/micro-test-plus/inlines/{test-reporter-inlines.h → reporter-inlines.h} +176 -106
- package/include/micro-os-plus/micro-test-plus/inlines/{test-suite-inlines.h → runner-inlines.h} +41 -43
- package/include/micro-os-plus/micro-test-plus/inlines/test-inlines.h +369 -0
- package/include/micro-os-plus/micro-test-plus/inlines/utility-inlines.h +126 -0
- package/include/micro-os-plus/micro-test-plus/literals.h +4 -3
- package/include/micro-os-plus/micro-test-plus/math.h +9 -6
- package/include/micro-os-plus/micro-test-plus/operators.h +38 -44
- package/include/micro-os-plus/micro-test-plus/reflection.h +15 -4
- package/include/micro-os-plus/micro-test-plus/{test-reporter-basic.h → reporter-human.h} +72 -72
- package/include/micro-os-plus/micro-test-plus/{test-reporter-tap.h → reporter-tap.h} +69 -69
- package/include/micro-os-plus/micro-test-plus/{test-reporter.h → reporter.h} +296 -200
- package/include/micro-os-plus/micro-test-plus/runner-totals.h +264 -0
- package/include/micro-os-plus/micro-test-plus/runner.h +453 -0
- package/include/micro-os-plus/micro-test-plus/test.h +1069 -0
- package/include/micro-os-plus/micro-test-plus/timings.h +366 -0
- package/include/micro-os-plus/micro-test-plus/type-traits.h +239 -545
- package/include/micro-os-plus/micro-test-plus/utility.h +135 -0
- package/include/micro-os-plus/micro-test-plus.h +25 -228
- package/meson.build +10 -6
- package/package.json +1 -1
- package/src/deferred-reporter.cpp +118 -0
- package/src/reflection.cpp +95 -0
- package/src/reporter-human.cpp +822 -0
- package/src/reporter-tap.cpp +782 -0
- package/src/reporter.cpp +676 -0
- package/src/runner-totals.cpp +95 -0
- package/src/runner.cpp +563 -0
- package/src/test.cpp +496 -0
- package/src/timings.cpp +209 -0
- package/src/utility.cpp +163 -0
- package/.cmake-format.yaml +0 -11
- package/include/micro-os-plus/micro-test-plus/inlines/micro-test-plus-inlines.h +0 -313
- package/include/micro-os-plus/micro-test-plus/test-runner.h +0 -281
- package/include/micro-os-plus/micro-test-plus/test-suite.h +0 -492
- package/src/micro-test-plus.cpp +0 -316
- package/src/test-reporter-basic.cpp +0 -466
- package/src/test-reporter-tap.cpp +0 -530
- package/src/test-reporter.cpp +0 -399
- package/src/test-runner.cpp +0 -311
- 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
|
+
// ----------------------------------------------------------------------------
|