Skip to main content

reporter-tap.cpp File

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

Included Headers

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#if defined(MICRO_OS_PLUS_TRACE)
50#include <micro-os-plus/diag/trace.h>
51#endif // MICRO_OS_PLUS_TRACE
52
56
57// For the PRIu32 macro used in snprintf() formatting of uint32_t values.
58#include <cinttypes>
59
60// ----------------------------------------------------------------------------
61
62#if defined(__GNUC__)
63#pragma GCC diagnostic ignored "-Waggregate-return"
64#if defined(__clang__)
65#pragma clang diagnostic ignored "-Wunknown-warning-option"
66#pragma clang diagnostic ignored "-Wc++98-compat"
67#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
68#endif
69#endif
70
71// =============================================================================
72
74{
75 // --------------------------------------------------------------------------
76
84 std::unique_ptr<std::vector<std::string_view>> argvs)
85 : reporter{ std::move (argvs) }
86 {
87#if defined(MICRO_OS_PLUS_TRACE) \
88 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
89 trace::printf ("%s\n", __PRETTY_FUNCTION__);
90#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
91 }
92
100 {
101#if defined(MICRO_OS_PLUS_TRACE) \
102 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
103 trace::printf ("%s\n", __PRETTY_FUNCTION__);
104#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
105 }
106
107 // --------------------------------------------------------------------------
108
109 constexpr size_t indent_size = 4;
110
120 {
121 buffer_.append (m.level * indent_size, ' ');
122 return *this;
123 }
124
125 // --------------------------------------------------------------------------
126
136 void
138 {
139#if defined(MICRO_OS_PLUS_TRACE) \
140 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
141 trace::printf ("%s\n", __PRETTY_FUNCTION__);
142#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
143
144#if defined(__GNUC__)
145#pragma GCC diagnostic push
146#if defined(__clang__)
147#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
148#endif
149#endif
150
152 {
153 printf ("\n");
154 }
155
156 write_info_ ();
157
158 const char* message = "TAP version 14";
159 if (output_file_ != nullptr)
160 {
161 fprintf (output_file_, "%s\n", message);
162 }
163
165 {
166 printf ("%s\n", message);
167
168 flush ();
169 }
170
171 add_empty_line_ = false;
172
173#if defined(__GNUC__)
174#pragma GCC diagnostic pop
175#endif
176 }
177
187 void
189 {
190#if defined(MICRO_OS_PLUS_TRACE) \
191 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
192 trace::printf ("%s\n", __PRETTY_FUNCTION__);
193#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
194
195#if defined(__GNUC__)
196#pragma GCC diagnostic push
197#if defined(__clang__)
198#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
199#endif
200#endif
201
202 size_t total_suites_count = runner.total_suites_count ();
203
204 uint32_t milliseconds = 0;
205 uint32_t microseconds = 0;
207 {
208 runner.timings ().compute_elapsed_time (milliseconds, microseconds);
209 }
210
211 char message_summary[32];
212 snprintf (message_summary, sizeof (message_summary), "1..%zu",
213 total_suites_count);
214
215 char message_totals[160];
216 snprintf (message_totals, sizeof (message_totals),
217 "total: %zu check%s passed, %zu failed, in %zu test "
218 "case%s, %zu test suite%s",
220 runner.totals ().successful_checks () == 1 ? "" : "s",
223 runner.totals ().executed_subtests () == 1 ? "" : "s",
224 total_suites_count, total_suites_count == 1 ? "" : "s");
225
226 char message_time[120] = "";
227 if (milliseconds > 0 || microseconds > 0)
228 {
229 snprintf (message_time, sizeof (message_time),
230 ", time: %" PRIu32 ".%03" PRIu32 " ms", milliseconds,
231 microseconds);
232 }
233
234 if (output_file_ != nullptr)
235 {
236 fprintf (output_file_, "%s\n# { %s%s }\n", message_summary,
237 message_totals, message_time);
238 }
239
241 {
243 {
244 printf ("\n");
245 }
246
248 {
249 printf ("%s\n", message_summary);
250 }
251 else
252 {
253 // With quiet verbosity, there are no ok/not ok lines, so the TAP
254 // plan should look like a skipped test.
255 printf ("1..0\n");
256 }
257
258 printf ("# { %s%s }\n", message_totals, message_time);
259
260 flush ();
261 }
262
263#if defined(__GNUC__)
264#pragma GCC diagnostic pop
265#endif
266 }
267
268 // --------------------------------------------------------------------------
269
278 void
280 {
281#if defined(MICRO_OS_PLUS_TRACE) \
282 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
283#pragma GCC diagnostic push
284#if defined(__clang__)
285#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
286#endif
287 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
288#pragma GCC diagnostic pop
289#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
290
291#if defined(__GNUC__)
292#pragma GCC diagnostic push
293#if defined(__clang__)
294#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
295#endif
296#endif
297
298 char message_subtest[120];
299 snprintf (message_subtest, sizeof (message_subtest), "# Subtest: %s",
300 suite.name ());
301
302 if (output_file_ != nullptr)
303 {
304 fprintf (output_file_, "%s\n", message_subtest);
305 }
306
308 {
310 {
311 printf ("\n");
312 }
313
314 printf ("%s\n", message_subtest);
315
316 flush ();
317
318 add_empty_line_ = true;
319 }
320
321#if defined(__GNUC__)
322#pragma GCC diagnostic pop
323#endif
324 }
325
335 void
337 {
338#if defined(MICRO_OS_PLUS_TRACE) \
339 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
340#pragma GCC diagnostic push
341#if defined(__clang__)
342#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
343#endif
344 trace::printf (
345 "%s '%s' +%zu -%zu in xc%zu, xs%zu | cs%zu\n", __PRETTY_FUNCTION__,
350
351#pragma GCC diagnostic pop
352#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
353
354#if defined(__GNUC__)
355#pragma GCC diagnostic push
356#if defined(__clang__)
357#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
358#endif
359#endif
360
361 uint32_t milliseconds = 0;
362 uint32_t microseconds = 0;
364 {
365 suite.timings ().compute_elapsed_time (milliseconds, microseconds);
366 }
367
368 std::string indent (indent_size, ' ');
369
370 char message_summary[40];
371 snprintf (message_summary, sizeof (message_summary), "%s1..%zu",
373 char message_totals[120];
374 if (suite.totals ().was_successful ()) [[likely]]
375 {
376 snprintf (message_totals, sizeof (message_totals),
377 "ok %zu - %s # { passed, %zu check%s in %zu "
378 "test case%s",
381 suite.totals ().successful_checks () == 1 ? "" : "s",
383 suite.totals ().executed_subtests () == 1 ? "" : "s");
384 }
385 else
386 {
387 snprintf (message_totals, sizeof (message_totals),
388 "not ok %zu - %s # { FAILED, %zu check%s "
389 "passed, %zu failed, in %zu test case%s",
392 suite.totals ().successful_checks () == 1 ? "" : "s",
395 suite.totals ().executed_subtests () == 1 ? "" : "s");
396 }
397
398 char message_time[120] = "";
399 if (milliseconds > 0 || microseconds > 0)
400 {
401 snprintf (message_time, sizeof (message_time),
402 ", time: %" PRIu32 ".%03" PRIu32 " ms", milliseconds,
403 microseconds);
404 }
405
406 if (output_file_ != nullptr)
407 {
409
410 fprintf (output_file_, "%s\n%s%s }\n", message_summary, message_totals,
411 message_time);
412 }
413
414 // At this point, the buffer may contain output from the test case, which
415 // should be displayed.
417 {
419 {
420 printf ("\n");
421 }
422
423 if (suite.totals ().was_successful ()) [[likely]]
424 {
425 // Successful test suite.
426
428 {
429 // With verbosity, show full TAP output accumulated in the
430 // buffer.
432 }
433
434 printf ("%s\n%s%s }\n", message_summary, message_totals,
435 message_time);
436 }
437 else
438 {
439 // Failed test suite.
440
441 // Show full TAP output accumulated in the buffer for failed suite
442 // cases, as it may contain useful information about the failure.
444
445 printf ("%s\n%s%s }\n", message_summary, message_totals,
446 message_time);
447 }
448
449 flush ();
450 }
451
452 buffer_.clear ();
453
454 add_empty_line_ = true;
455
456#if defined(__GNUC__)
457#pragma GCC diagnostic pop
458#endif
459 }
460
461 // --------------------------------------------------------------------------
462
471 void
473 {
474#if defined(MICRO_OS_PLUS_TRACE) \
475 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
476#pragma GCC diagnostic push
477#if defined(__clang__)
478#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
479#endif
480 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, subtest.name ());
481#pragma GCC diagnostic pop
482#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
483
484#if defined(__GNUC__)
485#pragma GCC diagnostic push
486#if defined(__clang__)
487#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
488#endif
489#endif
490
491 if (!buffer_.empty ())
492 {
493 // Each suite should start with an empty buffer.
495 flush ();
496 abort ();
497 }
498
499 std::string indent (indent_size * subtest.nesting_depth (), ' ');
500
501 char message_subtest[120];
502 snprintf (message_subtest, sizeof (message_subtest), "%s# Subtest: %s",
503 indent.c_str (), subtest.name ());
504
505 if (output_file_ != nullptr)
506 {
507 fprintf (output_file_, "%s\n", message_subtest);
508 }
509
511 {
513 {
514 printf ("\n");
515 }
516
517 printf ("%s\n", message_subtest);
518
519 flush ();
520
521 add_empty_line_ = false;
522 }
523
524#if defined(__GNUC__)
525#pragma GCC diagnostic pop
526#endif
527 }
528
538 void
540 {
541#if defined(MICRO_OS_PLUS_TRACE) \
542 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
543#pragma GCC diagnostic push
544#if defined(__clang__)
545#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
546#endif
547 trace::printf ("%s '%s' i%zu +%zu -%zu in xc%zu, xs%zu | cs%zu\n",
548 __PRETTY_FUNCTION__, subtest.name (),
555#pragma GCC diagnostic pop
556#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
557
558#if defined(__GNUC__)
559#pragma GCC diagnostic push
560#if defined(__clang__)
561#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
562#endif
563#endif
564
565 std::string indent (indent_size * subtest.nesting_depth (), ' ');
566 std::string indent2 (indent_size * (subtest.nesting_depth () + 1), ' ');
567
568 char message_summary[40];
569 snprintf (message_summary, sizeof (message_summary), "%s1..%zu",
570 indent2.c_str (),
573
574 char message_totals[120];
575 if (subtest.totals ().was_successful ()) [[likely]]
576 {
577 snprintf (message_totals, sizeof (message_totals),
578 "%sok %zu - %s # { passed, %zu check%s }", indent.c_str (),
581 subtest.totals ().successful_checks () == 1 ? "" : "s");
582 }
583 else
584 {
585 snprintf (message_totals, sizeof (message_totals),
586 "%snot ok %zu - %s # { FAILED, %zu check%s "
587 "passed, %zu failed }",
588 indent.c_str (), subtest.own_index (), subtest.name (),
590 subtest.totals ().successful_checks () == 1 ? "" : "s",
592 }
593
594 if (output_file_ != nullptr)
595 {
597
598 fprintf (output_file_, "%s\n%s\n", message_summary, message_totals);
599 }
600
601 // At this point, the buffer may contain output from the subtest, which
602 // should be displayed.
604 {
606 {
607 printf ("\n");
608 }
609
610 if (subtest.totals ().was_successful ()) [[likely]]
611 {
612 // Successful subtest.
614 {
615 // With verbosity, show full TAP output accumulated in the
616 // buffer.
618
619 printf ("%s\n", message_summary);
620 }
621 else
622 {
623 // Without verbosity, show only the summary line
624 // and count only subtests, not checks, as the TAP output is
625 // not shown.
626 printf ("%s1..%zu\n", indent2.c_str (),
628 }
629
630 printf ("%s\n", message_totals);
631 }
632 else
633 {
634 // Failed subtest.
635
636 // Show full TAP output accumulated in the buffer for failed
637 // subtests, as it may contain useful information about the
638 // failure.
640
641 printf ("%s\n%s\n", message_summary, message_totals);
642 }
643
644 flush ();
645 }
646
647 buffer_.clear ();
648
649 add_empty_line_ = true;
650
651#if defined(__GNUC__)
652#pragma GCC diagnostic pop
653#endif
654 }
655
656 // --------------------------------------------------------------------------
657
664 const char*
666 {
667 return "# ";
668 }
669
682 void
684 {
685 size_t level = subtest.nesting_depth ();
686 *this << indent (level + 1) << "ok "
687 << static_cast<int> (subtest.current_subtest_index ()) << " - ";
688 if (!message.empty ())
689 {
690 *this << message.c_str ();
691 }
692 }
693
702 void
704 {
705 *this << endl;
706
707 flush ();
708 }
709
719 void
721 std::string& message, const bool has_expression,
722 [[maybe_unused]] const reflection::source_location& location,
724 {
725 size_t level = subtest.nesting_depth ();
726 *this << indent (level + 1) << "not ok "
727 << static_cast<int> (subtest.current_subtest_index ());
728
729 if (!message.empty ())
730 {
731 *this << " - " << message.c_str ();
732 }
733 *this << endl;
734
735 // https://testanything.org/tap-version-14-specification.html
736 // 2-space indentation for YAML diagnostics.
737 *this << indent (level + 1) << " ---";
738 if (has_expression)
739 {
740 *this << endl;
741 *this << indent (level + 1) << " condition: ";
742 }
743 }
744
751 void
753 const reflection::source_location& location, bool abort,
755 {
756 size_t level = subtest.nesting_depth ();
757 if (abort)
758 {
759 *this << " aborted...";
760 }
761 *this << endl;
762
763 // https://testanything.org/tap-version-14-specification.html
764 // 2-space indentation for YAML diagnostics.
765
766 *this << indent (level + 1) << " at:" << endl;
767 *this << indent (level + 1)
768 << " filename: " << reflection::short_name (location.file_name ())
769 << endl;
770 *this << indent (level + 1) << " line: " << location.line () << endl;
771
772 *this << indent (level + 1) << " ..." << endl;
773
774 flush ();
775 }
776
777 // --------------------------------------------------------------------------
778} // namespace micro_os_plus::micro_test_plus
779
780// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.0 by Doxygen 1.17.0.