@micro-os-plus/micro-test-plus 3.2.3 → 3.3.1

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.
@@ -0,0 +1,466 @@
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++ basic test
21
+ * reporter methods.
22
+ *
23
+ * @details
24
+ * This source file contains the implementations for `test_reporter_basic`,
25
+ * the default concrete implementation of the `test_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
+ #if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
40
+ #include <micro-os-plus/config.h>
41
+ #endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
42
+
43
+ #include <micro-os-plus/micro-test-plus.h>
44
+
45
+ // ----------------------------------------------------------------------------
46
+
47
+ #pragma GCC diagnostic ignored "-Waggregate-return"
48
+ #if defined(__clang__)
49
+ #pragma clang diagnostic ignored "-Wunknown-warning-option"
50
+ #pragma clang diagnostic ignored "-Wc++98-compat"
51
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
52
+ #endif
53
+
54
+ namespace micro_os_plus::micro_test_plus
55
+ {
56
+ // --------------------------------------------------------------------------
57
+
58
+ test_reporter_basic::test_reporter_basic (void)
59
+ {
60
+ colors_ = colors_red_green;
61
+ }
62
+
63
+ /**
64
+ * @details
65
+ * This method outputs the prefix for a passing test result, applying the
66
+ * appropriate colour formatting and symbols to clearly indicate success. If
67
+ * the output occurs within a test case, additional indentation is applied
68
+ * for readability. The prefix includes a tick symbol (`✓`) and, if provided,
69
+ * an associated message. Colour formatting is reset after the prefix to
70
+ * maintain consistent output style across all test cases and folders.
71
+ *
72
+ * The prefix/suffix methods help shorten the code
73
+ * generated by the template methods.
74
+ */
75
+ void
76
+ test_reporter_basic::output_pass_prefix_ (std::string& message)
77
+ {
78
+ *this << colors_.pass;
79
+ if (is_in_test_case_)
80
+ {
81
+ *this << " ";
82
+ }
83
+ *this << " ✓ ";
84
+ *this << colors_.none;
85
+ if (!message.empty ())
86
+ {
87
+ *this << message.c_str ();
88
+ }
89
+ }
90
+
91
+ /**
92
+ * @details
93
+ * The `endl` function acts as a stream manipulator for the `test_reporter`,
94
+ * inserting a line ending into the output buffer and flushing the current
95
+ * content if necessary. This ensures that test report output is clearly
96
+ * separated and formatted, improving readability and professionalism in the
97
+ * presentation of test results.
98
+ *
99
+ * Using `endl` in conjunction with the `test_reporter` output operators
100
+ * provides a familiar and convenient mechanism for managing line breaks,
101
+ * similar to standard C++ stream manipulators.
102
+ *
103
+ * The prefix/suffix methods help shorten the code
104
+ * generated by the template methods.
105
+ */
106
+ void
107
+ test_reporter_basic::output_pass_suffix_ (void)
108
+ {
109
+ *this << endl;
110
+
111
+ flush ();
112
+ }
113
+
114
+ /**
115
+ * @details
116
+ * This method outputs the prefix for a failing test result, applying the
117
+ * appropriate colour formatting and symbols to clearly indicate failure. If
118
+ * the output occurs within a test case, additional indentation is applied
119
+ * for readability. The prefix includes a cross symbol (`✗`), an optional
120
+ * message, and the label "FAILED". The source location is appended in
121
+ * parentheses, showing the file or folder name and line number where the
122
+ * failure occurred. Colour formatting is reset after the prefix to maintain
123
+ * consistent output style across all test cases and folders.
124
+ */
125
+ void
126
+ test_reporter_basic::output_fail_prefix_ (
127
+ std::string& message, const bool hasExpression,
128
+ const reflection::source_location& location)
129
+ {
130
+ *this << colors_.fail;
131
+ if (is_in_test_case_)
132
+ {
133
+ *this << " ";
134
+ }
135
+ *this << " ✗ ";
136
+ *this << colors_.none;
137
+ if (!message.empty ())
138
+ {
139
+ *this << message.c_str ();
140
+ *this << " ";
141
+ }
142
+ *this << colors_.fail << "FAILED" << colors_.none;
143
+ #pragma GCC diagnostic push
144
+ #if defined(__clang__)
145
+ #pragma clang diagnostic ignored "-Wsign-conversion"
146
+ #elif defined(__GNUC__)
147
+ #pragma GCC diagnostic ignored "-Wnarrowing"
148
+ #pragma GCC diagnostic ignored "-Wsign-conversion"
149
+ #endif
150
+ *this << " (" << reflection::short_name (location.file_name ()) << ":"
151
+ << type_traits::genuine_integral_value<unsigned int>{
152
+ location.line ()
153
+ };
154
+ #pragma GCC diagnostic pop
155
+ if (hasExpression)
156
+ {
157
+ *this << ", ";
158
+ }
159
+ }
160
+
161
+ /**
162
+ * @details
163
+ * This method outputs the suffix for a failing test result by closing the
164
+ * location information, appending an "aborted..." message if the test was
165
+ * aborted, and then adding a newline to the test output. The output stream
166
+ * is flushed to ensure immediate visibility. This approach guarantees that
167
+ * failure results are clearly separated, promptly reported, and easily
168
+ * distinguishable across all test cases and folders.
169
+ */
170
+ void
171
+ test_reporter_basic::output_fail_suffix_ (
172
+ [[maybe_unused]] const reflection::source_location& location, bool abort)
173
+ {
174
+ *this << ")";
175
+ if (abort)
176
+ {
177
+ *this << " aborted...";
178
+ }
179
+ *this << endl;
180
+
181
+ flush ();
182
+ }
183
+
184
+ /**
185
+ * @details
186
+ * This method appends a newline character to the internal output buffer of
187
+ * the `test_reporter` and immediately flushes the stream. This ensures that
188
+ * each line of test output is clearly separated and promptly displayed,
189
+ * enhancing the readability and organisation of test results across all test
190
+ * cases and folders.
191
+ */
192
+ void
193
+ test_reporter_basic::endline (void)
194
+ {
195
+ out_.append ("\n");
196
+ flush ();
197
+ }
198
+
199
+ /**
200
+ * @details
201
+ * This method flushes the output buffer of the `test_reporter` by
202
+ * synchronising it with the standard output stream. This guarantees that all
203
+ * pending test output is immediately written and visible, ensuring prompt
204
+ * and reliable reporting of test results across all test cases and folders.
205
+ */
206
+ void
207
+ test_reporter_basic::flush (void)
208
+ {
209
+ fflush (stdout); // Sync STDOUT.
210
+ }
211
+
212
+ /**
213
+ * @details
214
+ * This method marks the beginning of a test case, setting the internal state
215
+ * to indicate that test output is now within a test case context. If there
216
+ * is pending output and the verbosity level is set to verbose, it ensures
217
+ * that output is properly separated and displayed, adding an empty line if
218
+ * necessary. The output buffer is cleared and the stream is flushed to
219
+ * guarantee that all previous output is visible before the new test case
220
+ * begins. This approach enhances the clarity and organisation of test
221
+ * results across all test cases and folders.
222
+ */
223
+ void
224
+ test_reporter_basic::begin_test_case ([[maybe_unused]] const char* name)
225
+ {
226
+ is_in_test_case_ = true;
227
+
228
+ if (!out_.empty () && (verbosity == verbosity::verbose))
229
+ {
230
+ if (add_empty_line)
231
+ {
232
+ printf ("\n");
233
+ }
234
+ output ();
235
+ add_empty_line = true;
236
+ }
237
+
238
+ out_.clear ();
239
+
240
+ flush ();
241
+ }
242
+
243
+ /**
244
+ * @details
245
+ * This method marks the end of a test case, summarising its outcome and
246
+ * outputting the results with appropriate formatting and colour coding. If
247
+ * any checks have failed, a failure message is displayed, including the
248
+ * number of successful and failed checks. For passing test cases, a success
249
+ * message is shown with the total number of checks. The output is adjusted
250
+ * according to the verbosity level, and additional spacing is managed for
251
+ * clarity. The output buffer is cleared and the stream is flushed to ensure
252
+ * all results are immediately visible, supporting clear and organised
253
+ * reporting across all test cases and folders.
254
+ */
255
+ void
256
+ test_reporter_basic::end_test_case ([[maybe_unused]] const char* name)
257
+ {
258
+ if (verbosity == verbosity::normal || verbosity == verbosity::verbose)
259
+ {
260
+ if (current_test_suite->current_test_case.failed_checks > 0)
261
+ {
262
+ if (true /* add_empty_line */)
263
+ {
264
+ printf ("\n");
265
+ }
266
+ #pragma GCC diagnostic push
267
+ #if defined(__clang__)
268
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
269
+ #endif
270
+ printf (" • %s - test case started\n", name);
271
+ output ();
272
+ printf (
273
+ " %s✗%s %s - test case %sFAILED%s (%zu check%s passed, %zu "
274
+ "failed)\n",
275
+ colors_.fail, colors_.none, name, colors_.fail, colors_.none,
276
+ current_test_suite->current_test_case.successful_checks,
277
+ current_test_suite->current_test_case.successful_checks == 1
278
+ ? ""
279
+ : "s",
280
+ current_test_suite->current_test_case.failed_checks);
281
+ #pragma GCC diagnostic pop
282
+ add_empty_line = true;
283
+ }
284
+ else
285
+ {
286
+ if (add_empty_line)
287
+ {
288
+ printf ("\n");
289
+ }
290
+ if (verbosity == verbosity::verbose)
291
+ {
292
+ #pragma GCC diagnostic push
293
+ #if defined(__clang__)
294
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
295
+ #endif
296
+ printf (" • %s - test case started\n", name);
297
+ output ();
298
+ printf (
299
+ " %s✓%s %s - test case passed (%zu check%s)\n",
300
+ colors_.pass, colors_.none, name,
301
+ current_test_suite->current_test_case.successful_checks,
302
+ current_test_suite->current_test_case.successful_checks
303
+ == 1
304
+ ? ""
305
+ : "s");
306
+ #pragma GCC diagnostic pop
307
+ add_empty_line = true;
308
+ }
309
+ else
310
+ {
311
+ #pragma GCC diagnostic push
312
+ #if defined(__clang__)
313
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
314
+ #endif
315
+ printf (
316
+ " %s✓%s %s - test case passed (%zu check%s)\n",
317
+ colors_.pass, colors_.none, name,
318
+ current_test_suite->current_test_case.successful_checks,
319
+ current_test_suite->current_test_case.successful_checks
320
+ == 1
321
+ ? ""
322
+ : "s");
323
+ #pragma GCC diagnostic pop
324
+
325
+ add_empty_line = false;
326
+ }
327
+ }
328
+ }
329
+
330
+ out_.clear ();
331
+ flush ();
332
+
333
+ is_in_test_case_ = false;
334
+ }
335
+
336
+ /**
337
+ * @details
338
+ * This method marks the beginning of a test suite, ensuring that output is
339
+ * properly separated and clearly presented. If there is pending output, the
340
+ * stream is flushed and an empty line is added for clarity. For silent or
341
+ * quiet verbosity levels, output is suppressed. Otherwise, a message
342
+ * indicating the start of the test suite is displayed. This approach
343
+ * enhances the organisation and readability of test results across all test
344
+ * cases and folders.
345
+ */
346
+ void
347
+ test_reporter_basic::begin_test_suite (const char* name)
348
+ {
349
+ if (add_empty_line)
350
+ {
351
+ flush ();
352
+ printf ("\n");
353
+ }
354
+
355
+ if (verbosity == verbosity::silent || verbosity == verbosity::quiet)
356
+ {
357
+ add_empty_line = false;
358
+ return;
359
+ }
360
+
361
+ #pragma GCC diagnostic push
362
+ #if defined(__clang__)
363
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
364
+ #endif
365
+ printf ("• %s - test suite started\n", name);
366
+ #pragma GCC diagnostic pop
367
+
368
+ add_empty_line = true;
369
+ }
370
+
371
+ /**
372
+ * @details
373
+ * This method marks the end of a test suite, summarising the overall results
374
+ * and presenting them with appropriate formatting and colour coding. If the
375
+ * suite contains test cases and the verbosity is not set to quiet, an empty
376
+ * line is added for clarity. For suites with no failed checks and at least
377
+ * one successful check, a success message is displayed, including the number
378
+ * of checks and test cases. Otherwise, a failure message is shown, detailing
379
+ * the number of successful and failed checks, as well as the total number of
380
+ * test cases. The output is immediately flushed to ensure prompt and
381
+ * organised reporting across all test cases and folders.
382
+ */
383
+ void
384
+ test_reporter_basic::end_test_suite (test_suite_base& suite)
385
+ {
386
+ if (verbosity == verbosity::silent)
387
+ {
388
+ return;
389
+ }
390
+
391
+ if (suite.test_cases_count () > 0 && verbosity != verbosity::quiet)
392
+ {
393
+ printf ("\n");
394
+ add_empty_line = true;
395
+ }
396
+
397
+ // Also fail if none passed.
398
+ if (suite.failed_checks () == 0 && suite.successful_checks () != 0)
399
+ {
400
+ #pragma GCC diagnostic push
401
+ #if defined(__clang__)
402
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
403
+ #endif
404
+ printf (
405
+ "%s✓%s %s - test suite passed (%zu check%s in %zu test case%s)\n",
406
+ colors_.pass, colors_.none, suite.name (),
407
+ suite.successful_checks (),
408
+ suite.successful_checks () == 1 ? "" : "s",
409
+ suite.test_cases_count (),
410
+ suite.test_cases_count () == 1 ? "" : "s");
411
+ #pragma GCC diagnostic pop
412
+ }
413
+ else
414
+ {
415
+ #pragma GCC diagnostic push
416
+ #if defined(__clang__)
417
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
418
+ #endif
419
+ printf ("%s✗%s %s - test suite %sFAILED%s (%zu check%s passed, %zu "
420
+ "failed, "
421
+ "in %zu test case%s)\n",
422
+ colors_.fail, colors_.none, suite.name (), colors_.fail,
423
+ colors_.none, suite.successful_checks (),
424
+ suite.successful_checks () == 1 ? "" : "s",
425
+ suite.failed_checks (), suite.test_cases_count (),
426
+ suite.test_cases_count () == 1 ? "" : "s");
427
+ #pragma GCC diagnostic pop
428
+ }
429
+ flush ();
430
+ }
431
+
432
+ void
433
+ test_reporter_basic::begin_test ([[maybe_unused]] size_t test_suites_count)
434
+ {
435
+ // Nothing to do.
436
+ }
437
+
438
+ #pragma GCC diagnostic push
439
+ #pragma GCC diagnostic ignored "-Wshadow"
440
+ void
441
+ test_reporter_basic::end_test ([[maybe_unused]] test_runner& runner)
442
+ {
443
+ // Nothing to do.
444
+ }
445
+ #pragma GCC diagnostic pop
446
+
447
+ /**
448
+ * @details
449
+ * This method writes the contents of the internal output buffer to the
450
+ * standard output stream without appending a newline character. After
451
+ * outputting the buffer, it is cleared to prepare for subsequent output.
452
+ * This approach ensures that test results are presented promptly and
453
+ * efficiently, supporting clear and organised reporting across all test
454
+ * cases and folders.
455
+ */
456
+ void
457
+ test_reporter_basic::output (void)
458
+ {
459
+ printf ("%s", out_.c_str ()); // No `\n` here.
460
+ out_.clear ();
461
+ }
462
+
463
+ // --------------------------------------------------------------------------
464
+ } // namespace micro_os_plus::micro_test_plus
465
+
466
+ // ----------------------------------------------------------------------------