Skip to main content

reporter-tap.cpp File

C++ source file with implementations for the µTest++ TAP suite reporter methods. More...

Included Headers

#include <micro-os-plus/diag/trace.h> #include "micro-os-plus/micro-test-plus/reporter-tap.h" #include "micro-os-plus/micro-test-plus/runner.h" #include "micro-os-plus/micro-test-plus/test.h" #include <cinttypes>

Namespaces Index

namespacemicro_os_plus

The primary namespace for the µOS++ framework. More...

namespacemicro_test_plus

Primary namespace for the µTest++ testing framework. More...

Description

C++ source file with implementations for the µTest++ TAP suite reporter methods.

This source file contains the implementations for reporter_tap, a concrete implementation of the reporter abstract interface that formats suite results according to the Test Anything Protocol (TAP).

All definitions reside within the micro_os_plus::micro_test_plus namespace, ensuring clear separation from user code and minimising the risk of naming conflicts.

This file must be included when building the µTest++ library.

File Listing

The file content with the documentation metadata removed is:

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 Software License,
13 * which can be obtained from https://www.boost.org/LICENSE_1_0.txt.
14 */
15
16// ----------------------------------------------------------------------------
17
34
35// ----------------------------------------------------------------------------
36
37#if __has_include(<micro-os-plus/project-config.h>)
38#include <micro-os-plus/project-config.h>
39#elif __has_include(<micro-os-plus/config.h>)
40#pragma message \
41 "micro-os-plus/config.h is deprecated, rename to micro-os-plus/project-config.h and include it instead of micro-os-plus/config.h"
42#include <micro-os-plus/config.h>
43#endif // __has_include(<micro-os-plus/project-config.h>)
44
45#if __has_include(<micro-os-plus/micro-test-plus-defines.h>)
46#include <micro-os-plus/micro-test-plus-defines.h>
47#endif // __has_include(<micro-os-plus/micro-test-plus-defines.h>)
48
49#include <micro-os-plus/diag/trace.h>
50
54
55// For the PRIu32 macro used in snprintf() formatting of uint32_t values.
56#include <cinttypes>
57
58// ----------------------------------------------------------------------------
59
60#if defined(__GNUC__)
61#pragma GCC diagnostic ignored "-Waggregate-return"
62#if defined(__clang__)
63#pragma clang diagnostic ignored "-Wunknown-warning-option"
64#pragma clang diagnostic ignored "-Wc++98-compat"
65#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
66#endif
67#endif
68
69// =============================================================================
70
72{
73 // --------------------------------------------------------------------------
74
82 std::unique_ptr<std::vector<std::string_view>> argvs)
83 : reporter{ std::move (argvs) }
84 {
85#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
86 trace::printf ("%s\n", __PRETTY_FUNCTION__);
87#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
88 }
89
97 {
98#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
99 trace::printf ("%s\n", __PRETTY_FUNCTION__);
100#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
101 }
102
103 // --------------------------------------------------------------------------
104
105 constexpr size_t indent_size = 4;
106
116 {
117 buffer_.append (m.level * indent_size, ' ');
118 return *this;
119 }
120
121 // --------------------------------------------------------------------------
122
132 void
134 {
135#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
136 trace::printf ("%s\n", __PRETTY_FUNCTION__);
137#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
138
139#if defined(__GNUC__)
140#pragma GCC diagnostic push
141#if defined(__clang__)
142#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
143#endif
144#endif
145
147 {
148 printf ("\n");
149 }
150
151 write_info_ ();
152
153 const char* message = "TAP version 14";
154 if (output_file_ != nullptr)
155 {
156 fprintf (output_file_, "%s\n", message);
157 }
158
160 {
161 printf ("%s\n", message);
162
163 flush ();
164 }
165
166 add_empty_line_ = false;
167
168#if defined(__GNUC__)
169#pragma GCC diagnostic pop
170#endif
171 }
172
182 void
184 {
185#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
186 trace::printf ("%s\n", __PRETTY_FUNCTION__);
187#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
188
189#if defined(__GNUC__)
190#pragma GCC diagnostic push
191#if defined(__clang__)
192#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
193#endif
194#endif
195
196 size_t total_suites_count = runner.total_suites_count ();
197
198 uint32_t milliseconds = 0;
199 uint32_t microseconds = 0;
201 {
202 runner.timings ().compute_elapsed_time (milliseconds, microseconds);
203 }
204
205 char message_summary[32];
206 snprintf (message_summary, sizeof (message_summary), "1..%zu",
207 total_suites_count);
208
209 char message_totals[160];
210 snprintf (message_totals, sizeof (message_totals),
211 "total: %zu check%s passed, %zu failed, in %zu test "
212 "case%s, %zu test suite%s",
214 runner.totals ().successful_checks () == 1 ? "" : "s",
217 runner.totals ().executed_subtests () == 1 ? "" : "s",
218 total_suites_count, total_suites_count == 1 ? "" : "s");
219
220 char message_time[120] = "";
221 if (milliseconds > 0 || microseconds > 0)
222 {
223 snprintf (message_time, sizeof (message_time),
224 ", time: %" PRIu32 ".%03" PRIu32 " ms", milliseconds,
225 microseconds);
226 }
227
228 if (output_file_ != nullptr)
229 {
230 fprintf (output_file_, "%s\n# { %s%s }\n", message_summary,
231 message_totals, message_time);
232 }
233
235 {
237 {
238 printf ("\n");
239 }
240
242 {
243 printf ("%s\n", message_summary);
244 }
245 else
246 {
247 // With quiet verbosity, there are no ok/not ok lines, so the TAP
248 // plan should look like a skipped test.
249 printf ("1..0\n");
250 }
251
252 printf ("# { %s%s }\n", message_totals, message_time);
253
254 flush ();
255 }
256
257#if defined(__GNUC__)
258#pragma GCC diagnostic pop
259#endif
260 }
261
262 // --------------------------------------------------------------------------
263
272 void
274 {
275#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
276#pragma GCC diagnostic push
277#if defined(__clang__)
278#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
279#endif
280 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
281#pragma GCC diagnostic pop
282#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
283
284#if defined(__GNUC__)
285#pragma GCC diagnostic push
286#if defined(__clang__)
287#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
288#endif
289#endif
290
291 char message_subtest[120];
292 snprintf (message_subtest, sizeof (message_subtest), "# Subtest: %s",
293 suite.name ());
294
295 if (output_file_ != nullptr)
296 {
297 fprintf (output_file_, "%s\n", message_subtest);
298 }
299
301 {
303 {
304 printf ("\n");
305 }
306
307 printf ("%s\n", message_subtest);
308
309 flush ();
310
311 add_empty_line_ = true;
312 }
313
314#if defined(__GNUC__)
315#pragma GCC diagnostic pop
316#endif
317 }
318
328 void
330 {
331#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
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__,
342
343#pragma GCC diagnostic pop
344#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
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;
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",
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",
373 suite.totals ().successful_checks () == 1 ? "" : "s",
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",
384 suite.totals ().successful_checks () == 1 ? "" : "s",
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 {
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.
409 {
411 {
412 printf ("\n");
413 }
414
415 if (suite.totals ().was_successful ()) [[likely]]
416 {
417 // Successful test suite.
418
420 {
421 // With verbosity, show full TAP output accumulated in the
422 // buffer.
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.
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
463 void
465 {
466#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
467#pragma GCC diagnostic push
468#if defined(__clang__)
469#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
470#endif
471 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, subtest.name ());
472#pragma GCC diagnostic pop
473#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
474
475#if defined(__GNUC__)
476#pragma GCC diagnostic push
477#if defined(__clang__)
478#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
479#endif
480#endif
481
482 if (!buffer_.empty ())
483 {
484 // Each suite should start with an empty buffer.
486 flush ();
487 abort ();
488 }
489
490 std::string indent (indent_size * subtest.nesting_depth (), ' ');
491
492 char message_subtest[120];
493 snprintf (message_subtest, sizeof (message_subtest), "%s# Subtest: %s",
494 indent.c_str (), subtest.name ());
495
496 if (output_file_ != nullptr)
497 {
498 fprintf (output_file_, "%s\n", message_subtest);
499 }
500
502 {
504 {
505 printf ("\n");
506 }
507
508 printf ("%s\n", message_subtest);
509
510 flush ();
511
512 add_empty_line_ = false;
513 }
514
515#if defined(__GNUC__)
516#pragma GCC diagnostic pop
517#endif
518 }
519
529 void
531 {
532#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
533#pragma GCC diagnostic push
534#if defined(__clang__)
535#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
536#endif
537 trace::printf ("%s '%s' i%zu +%zu -%zu in xc%zu, xs%zu | cs%zu\n",
538 __PRETTY_FUNCTION__, subtest.name (),
545#pragma GCC diagnostic pop
546#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
547
548#if defined(__GNUC__)
549#pragma GCC diagnostic push
550#if defined(__clang__)
551#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
552#endif
553#endif
554
555 std::string indent (indent_size * subtest.nesting_depth (), ' ');
556 std::string indent2 (indent_size * (subtest.nesting_depth () + 1), ' ');
557
558 char message_summary[40];
559 snprintf (message_summary, sizeof (message_summary), "%s1..%zu",
560 indent2.c_str (),
563
564 char message_totals[120];
565 if (subtest.totals ().was_successful ()) [[likely]]
566 {
567 snprintf (message_totals, sizeof (message_totals),
568 "%sok %zu - %s # { passed, %zu check%s }", indent.c_str (),
571 subtest.totals ().successful_checks () == 1 ? "" : "s");
572 }
573 else
574 {
575 snprintf (message_totals, sizeof (message_totals),
576 "%snot ok %zu - %s # { FAILED, %zu check%s "
577 "passed, %zu failed }",
578 indent.c_str (), subtest.own_index (), subtest.name (),
580 subtest.totals ().successful_checks () == 1 ? "" : "s",
582 }
583
584 if (output_file_ != nullptr)
585 {
587
588 fprintf (output_file_, "%s\n%s\n", message_summary, message_totals);
589 }
590
591 // At this point, the buffer may contain output from the subtest, which
592 // should be displayed.
594 {
596 {
597 printf ("\n");
598 }
599
600 if (subtest.totals ().was_successful ()) [[likely]]
601 {
602 // Successful subtest.
604 {
605 // With verbosity, show full TAP output accumulated in the
606 // buffer.
608
609 printf ("%s\n", message_summary);
610 }
611 else
612 {
613 // Without verbosity, show only the summary line
614 // and count only subtests, not checks, as the TAP output is
615 // not shown.
616 printf ("%s1..%zu\n", indent2.c_str (),
618 }
619
620 printf ("%s\n", message_totals);
621 }
622 else
623 {
624 // Failed subtest.
625
626 // Show full TAP output accumulated in the buffer for failed
627 // subtests, as it may contain useful information about the
628 // failure.
630
631 printf ("%s\n%s\n", message_summary, message_totals);
632 }
633
634 flush ();
635 }
636
637 buffer_.clear ();
638
639 add_empty_line_ = true;
640
641#if defined(__GNUC__)
642#pragma GCC diagnostic pop
643#endif
644 }
645
646 // --------------------------------------------------------------------------
647
654 const char*
656 {
657 return "# ";
658 }
659
672 void
674 {
675 size_t level = subtest.nesting_depth ();
676 *this << indent (level + 1) << "ok "
677 << static_cast<int> (subtest.current_subtest_index ()) << " - ";
678 if (!message.empty ())
679 {
680 *this << message.c_str ();
681 }
682 }
683
692 void
694 {
695 *this << endl;
696
697 flush ();
698 }
699
709 void
711 std::string& message, const bool has_expression,
712 [[maybe_unused]] const reflection::source_location& location,
714 {
715 size_t level = subtest.nesting_depth ();
716 *this << indent (level + 1) << "not ok "
717 << static_cast<int> (subtest.current_subtest_index ());
718
719 if (!message.empty ())
720 {
721 *this << " - " << message.c_str ();
722 }
723 *this << endl;
724
725 // https://testanything.org/tap-version-14-specification.html
726 // 2-space indentation for YAML diagnostics.
727 *this << indent (level + 1) << " ---";
728 if (has_expression)
729 {
730 *this << endl;
731 *this << indent (level + 1) << " condition: ";
732 }
733 }
734
741 void
743 const reflection::source_location& location, bool abort,
745 {
746 size_t level = subtest.nesting_depth ();
747 if (abort)
748 {
749 *this << " aborted...";
750 }
751 *this << endl;
752
753 // https://testanything.org/tap-version-14-specification.html
754 // 2-space indentation for YAML diagnostics.
755
756 *this << indent (level + 1) << " at:" << endl;
757 *this << indent (level + 1)
758 << " filename: " << reflection::short_name (location.file_name ())
759 << endl;
760 *this << indent (level + 1) << " line: " << location.line () << endl;
761
762 *this << indent (level + 1) << " ..." << endl;
763
764 flush ();
765 }
766
767 // --------------------------------------------------------------------------
768} // namespace micro_os_plus::micro_test_plus
769
770// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.1 by Doxygen 1.17.0.