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// For the PRIu32 macro used in snprintf() formatting of uint32_t values.
38#include <cinttypes>
39
40#if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
41#include <micro-os-plus/config.h>
42#endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
43
44#if defined(MICRO_OS_PLUS_TRACE)
45#include <micro-os-plus/diag/trace.h>
46#endif // MICRO_OS_PLUS_TRACE
47
51
52// ----------------------------------------------------------------------------
53
54#if defined(__GNUC__)
55#pragma GCC diagnostic ignored "-Waggregate-return"
56#if defined(__clang__)
57#pragma clang diagnostic ignored "-Wunknown-warning-option"
58#pragma clang diagnostic ignored "-Wc++98-compat"
59#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
60#endif
61#endif
62
63// =============================================================================
64
66{
67 // --------------------------------------------------------------------------
68
76 std::unique_ptr<std::vector<std::string_view>> argvs)
77 : reporter{ std::move (argvs) }
78 {
79#if defined(MICRO_OS_PLUS_TRACE) \
80 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
81 trace::printf ("%s\n", __PRETTY_FUNCTION__);
82#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
83 }
84
92 {
93#if defined(MICRO_OS_PLUS_TRACE) \
94 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
95 trace::printf ("%s\n", __PRETTY_FUNCTION__);
96#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
97 }
98
99 // --------------------------------------------------------------------------
100
101 constexpr size_t indent_size = 4;
102
112 {
113 buffer_.append (m.level * indent_size, ' ');
114 return *this;
115 }
116
117 // --------------------------------------------------------------------------
118
128 void
130 {
131#if defined(MICRO_OS_PLUS_TRACE) \
132 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
133 trace::printf ("%s\n", __PRETTY_FUNCTION__);
134#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
135
136#if defined(__GNUC__)
137#pragma GCC diagnostic push
138#if defined(__clang__)
139#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
140#endif
141#endif
142
144 {
145 printf ("\n");
146 }
147
148 write_info_ ();
149
150 const char* message = "TAP version 14";
151 if (output_file_ != nullptr)
152 {
153 fprintf (output_file_, "%s\n", message);
154 }
155
157 {
158 printf ("%s\n", message);
159
160 flush ();
161 }
162
163 add_empty_line_ = false;
164
165#if defined(__GNUC__)
166#pragma GCC diagnostic pop
167#endif
168 }
169
179 void
181 {
182#if defined(MICRO_OS_PLUS_TRACE) \
183 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
184 trace::printf ("%s\n", __PRETTY_FUNCTION__);
185#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
186
187#if defined(__GNUC__)
188#pragma GCC diagnostic push
189#if defined(__clang__)
190#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
191#endif
192#endif
193
194 size_t total_suites_count = runner.total_suites_count ();
195
196 uint32_t milliseconds = 0;
197 uint32_t microseconds = 0;
199 {
200 runner.timings ().compute_elapsed_time (milliseconds, microseconds);
201 }
202
203 char message_summary[32];
204 snprintf (message_summary, sizeof (message_summary), "1..%zu",
205 total_suites_count);
206
207 char message_totals[160];
208 snprintf (message_totals, sizeof (message_totals),
209 "total: %zu check%s passed, %zu failed, in %zu test "
210 "case%s, %zu test suite%s",
212 runner.totals ().successful_checks () == 1 ? "" : "s",
215 runner.totals ().executed_subtests () == 1 ? "" : "s",
216 total_suites_count, total_suites_count == 1 ? "" : "s");
217
218 char message_time[120] = "";
219 if (milliseconds > 0 || microseconds > 0)
220 {
221 snprintf (message_time, sizeof (message_time),
222 ", time: %" PRIu32 ".%03" PRIu32 " ms", milliseconds,
223 microseconds);
224 }
225
226 if (output_file_ != nullptr)
227 {
228 fprintf (output_file_, "%s\n# { %s%s }\n", message_summary,
229 message_totals, message_time);
230 }
231
233 {
235 {
236 printf ("\n");
237 }
238
240 {
241 printf ("%s\n", message_summary);
242 }
243 else
244 {
245 // With quiet verbosity, there are no ok/not ok lines, so the TAP
246 // plan should look like a skipped test.
247 printf ("1..0\n");
248 }
249
250 printf ("# { %s%s }\n", message_totals, message_time);
251
252 flush ();
253 }
254
255#if defined(__GNUC__)
256#pragma GCC diagnostic pop
257#endif
258 }
259
260 // --------------------------------------------------------------------------
261
270 void
272 {
273#if defined(MICRO_OS_PLUS_TRACE) \
274 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
275#pragma GCC diagnostic push
276#if defined(__clang__)
277#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
278#endif
279 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
280#pragma GCC diagnostic pop
281#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
282
283#if defined(__GNUC__)
284#pragma GCC diagnostic push
285#if defined(__clang__)
286#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
287#endif
288#endif
289
290 char message_subtest[120];
291 snprintf (message_subtest, sizeof (message_subtest), "# Subtest: %s",
292 suite.name ());
293
294 if (output_file_ != nullptr)
295 {
296 fprintf (output_file_, "%s\n", message_subtest);
297 }
298
300 {
302 {
303 printf ("\n");
304 }
305
306 printf ("%s\n", message_subtest);
307
308 flush ();
309
310 add_empty_line_ = true;
311 }
312
313#if defined(__GNUC__)
314#pragma GCC diagnostic pop
315#endif
316 }
317
327 void
329 {
330#if defined(MICRO_OS_PLUS_TRACE) \
331 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
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_TRACE_MICRO_TEST_PLUS
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_TRACE) \
467 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
468#pragma GCC diagnostic push
469#if defined(__clang__)
470#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
471#endif
472 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, subtest.name ());
473#pragma GCC diagnostic pop
474#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
475
476#if defined(__GNUC__)
477#pragma GCC diagnostic push
478#if defined(__clang__)
479#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
480#endif
481#endif
482
483 if (!buffer_.empty ())
484 {
485 // Each suite should start with an empty buffer.
487 flush ();
488 abort ();
489 }
490
491 std::string indent (indent_size * subtest.nesting_depth (), ' ');
492
493 char message_subtest[120];
494 snprintf (message_subtest, sizeof (message_subtest), "%s# Subtest: %s",
495 indent.c_str (), subtest.name ());
496
497 if (output_file_ != nullptr)
498 {
499 fprintf (output_file_, "%s\n", message_subtest);
500 }
501
503 {
505 {
506 printf ("\n");
507 }
508
509 printf ("%s\n", message_subtest);
510
511 flush ();
512
513 add_empty_line_ = false;
514 }
515
516#if defined(__GNUC__)
517#pragma GCC diagnostic pop
518#endif
519 }
520
530 void
532 {
533#if defined(MICRO_OS_PLUS_TRACE) \
534 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS)
535#pragma GCC diagnostic push
536#if defined(__clang__)
537#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
538#endif
539 trace::printf ("%s '%s' i%zu +%zu -%zu in xc%zu, xs%zu | cs%zu\n",
540 __PRETTY_FUNCTION__, subtest.name (),
547#pragma GCC diagnostic pop
548#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
549
550#if defined(__GNUC__)
551#pragma GCC diagnostic push
552#if defined(__clang__)
553#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
554#endif
555#endif
556
557 std::string indent (indent_size * subtest.nesting_depth (), ' ');
558 std::string indent2 (indent_size * (subtest.nesting_depth () + 1), ' ');
559
560 char message_summary[40];
561 snprintf (message_summary, sizeof (message_summary), "%s1..%zu",
562 indent2.c_str (),
565
566 char message_totals[120];
567 if (subtest.totals ().was_successful ()) [[likely]]
568 {
569 snprintf (message_totals, sizeof (message_totals),
570 "%sok %zu - %s # { passed, %zu check%s }", indent.c_str (),
573 subtest.totals ().successful_checks () == 1 ? "" : "s");
574 }
575 else
576 {
577 snprintf (message_totals, sizeof (message_totals),
578 "%snot ok %zu - %s # { FAILED, %zu check%s "
579 "passed, %zu failed }",
580 indent.c_str (), subtest.own_index (), subtest.name (),
582 subtest.totals ().successful_checks () == 1 ? "" : "s",
584 }
585
586 if (output_file_ != nullptr)
587 {
589
590 fprintf (output_file_, "%s\n%s\n", message_summary, message_totals);
591 }
592
593 // At this point, the buffer may contain output from the subtest, which
594 // should be displayed.
596 {
598 {
599 printf ("\n");
600 }
601
602 if (subtest.totals ().was_successful ()) [[likely]]
603 {
604 // Successful subtest.
606 {
607 // With verbosity, show full TAP output accumulated in the
608 // buffer.
610
611 printf ("%s\n", message_summary);
612 }
613 else
614 {
615 // Without verbosity, show only the summary line
616 // and count only subtests, not checks, as the TAP output is
617 // not shown.
618 printf ("%s1..%zu\n", indent2.c_str (),
620 }
621
622 printf ("%s\n", message_totals);
623 }
624 else
625 {
626 // Failed subtest.
627
628 // Show full TAP output accumulated in the buffer for failed
629 // subtests, as it may contain useful information about the
630 // failure.
632
633 printf ("%s\n%s\n", message_summary, message_totals);
634 }
635
636 flush ();
637 }
638
639 buffer_.clear ();
640
641 add_empty_line_ = true;
642
643#if defined(__GNUC__)
644#pragma GCC diagnostic pop
645#endif
646 }
647
648 // --------------------------------------------------------------------------
649
656 const char*
658 {
659 return "# ";
660 }
661
674 void
676 {
677 size_t level = subtest.nesting_depth ();
678 *this << indent (level + 1) << "ok "
679 << static_cast<int> (subtest.current_subtest_index ()) << " - ";
680 if (!message.empty ())
681 {
682 *this << message.c_str ();
683 }
684 }
685
694 void
696 {
697 *this << endl;
698
699 flush ();
700 }
701
711 void
713 std::string& message, const bool has_expression,
714 [[maybe_unused]] const reflection::source_location& location,
716 {
717 size_t level = subtest.nesting_depth ();
718 *this << indent (level + 1) << "not ok "
719 << static_cast<int> (subtest.current_subtest_index ());
720
721 if (!message.empty ())
722 {
723 *this << " - " << message.c_str ();
724 }
725 *this << endl;
726
727 // https://testanything.org/tap-version-14-specification.html
728 // 2-space indentation for YAML diagnostics.
729 *this << indent (level + 1) << " ---";
730 if (has_expression)
731 {
732 *this << endl;
733 *this << indent (level + 1) << " condition: ";
734 }
735 }
736
743 void
745 const reflection::source_location& location, bool abort,
747 {
748 size_t level = subtest.nesting_depth ();
749 if (abort)
750 {
751 *this << " aborted...";
752 }
753 *this << endl;
754
755 // https://testanything.org/tap-version-14-specification.html
756 // 2-space indentation for YAML diagnostics.
757
758 *this << indent (level + 1) << " at:" << endl;
759 *this << indent (level + 1)
760 << " filename: " << reflection::short_name (location.file_name ())
761 << endl;
762 *this << indent (level + 1) << " line: " << location.line () << endl;
763
764 *this << indent (level + 1) << " ..." << endl;
765
766 flush ();
767 }
768
769 // --------------------------------------------------------------------------
770} // namespace micro_os_plus::micro_test_plus
771
772// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.0 by Doxygen 1.17.0.