Skip to main content

reporter-tap.cpp File

C++ source file with implementations for the µTest++ TAP test reporter member functions. 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...

namespacedetail

Internal implementation details for the µTest++ framework. More...

namespaceanonymous{reporter-tap.cpp}

Local helpers of the TAP reporter. More...

Classes Index​

classtap_name

A name prepared for a single TAP line, without a copy when it needs no change. More...

Functions Index​

std::stringtap_text (std::string_view text, bool escape)

Prepares a text for a single TAP line. More...

std::stringyaml_double_quoted (std::string_view text)

Formats a text as a YAML double-quoted scalar. More...

std::stringyaml_scalar (std::string_view text)

Formats a text as a YAML scalar, double-quoted only when needed. More...

Description​

C++ source file with implementations for the µTest++ TAP test reporter member functions.

This source file contains the implementations for reporter_tap, a concrete implementation of the reporter abstract interface that formats test results according to the Test Anything Protocol (TAP), version 14. It is the default reporter, also selected with the --reporter=tap command-line option.

All definitions reside within the micro_os_plus::micro_test_plus::detail namespace, since the reporters are internal to the framework.

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

Functions​

tap_text()​

std::string micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::tap_text (std::string_view text, bool escape)

Prepares a text for a single TAP line.

Each line break (\\n, \\r, or the \\r\\n pair) is replaced by a space, so that the text does not end the line. When escape is true, each \\ is written as \\\\ and each # as \\#, as the TAP 14 specification recommends for the test point descriptions and the reasons, so that a # in the text does not start a directive (for example # TODO, which would hide a failure). The # Subtest: comments are not escaped, since the specification defines no escapes in comments, and the node-tap parser uses their text as it is.

Parameters
text

The text to prepare.

escape

Whether to escape the \\ and # characters.

Returns

The text, without line breaks, and escaped if requested.

Definition at line 104 of file reporter-tap.cpp.

104 tap_text (std::string_view text, bool escape)
105 {
106 std::string result;
107 result.reserve (text.size ());
108
109 for (size_t i = 0; i < text.size (); ++i)
110 {
111 const char c = text[i];
112 if (c == '\n' || c == '\r')
113 {
114 // The `\r\n` pair is a single line break.
115 if (c == '\r' && i + 1 < text.size () && text[i + 1] == '\n')
116 {
117 ++i;
118 }
119 result.push_back (' ');
120 }
121 else if (escape && (c == '\\' || c == '#'))
122 {
123 result.push_back ('\\');
124 result.push_back (c);
125 }
126 else
127 {
128 result.push_back (c);
129 }
130 }
131
132 return result;
133 }

Reference micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::tap_text.

Referenced by micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::tap_name::tap_name and micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::tap_text.

yaml_double_quoted()​

std::string micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_double_quoted (std::string_view text)

Formats a text as a YAML double-quoted scalar.

The text is enclosed in ", with each \\ written as \\\\, each " as \\", and each control character (including the line breaks, the tab, and DEL) as a YAML escape sequence (\\0, \\t, \\n, \\r, \\e, and so on, or \\xHH for the others), so that the value is a single line, and is valid YAML whatever it contains. As plain scalars, the values starting with {, [, or ', or containing : or #, would be rejected or truncated by the YAML parsers used by the TAP consumers. The bytes above 0x7F are written as they are, since they are expected to be parts of UTF-8 sequences.

Parameters
text

The text to format.

Returns

The double-quoted scalar.

Definition at line 294 of file reporter-tap.cpp.

294 yaml_double_quoted (std::string_view text)
295 {
296 constexpr std::string_view digits{ "0123456789ABCDEF" };
297
298 std::string result;
299 result.reserve (text.size () + 2);
300
301 result.push_back ('"');
302 for (const char c : text)
303 {
304 const auto u = static_cast<unsigned char> (c);
305 switch (c)
306 {
307 case '\\':
308 result.append ("\\\\");
309 break;
310 case '"':
311 result.append ("\\\"");
312 break;
313 case '\0':
314 result.append ("\\0");
315 break;
316 case '\a':
317 result.append ("\\a");
318 break;
319 case '\b':
320 result.append ("\\b");
321 break;
322 case '\t':
323 result.append ("\\t");
324 break;
325 case '\n':
326 result.append ("\\n");
327 break;
328 case '\v':
329 result.append ("\\v");
330 break;
331 case '\f':
332 result.append ("\\f");
333 break;
334 case '\r':
335 result.append ("\\r");
336 break;
337 case '\x1B':
338 result.append ("\\e");
339 break;
340 default:
341 if (u < 0x20 || u == 0x7F)
342 {
343 result.append ("\\x");
344 result.push_back (digits[static_cast<std::size_t> (u >> 4)]);
345 result.push_back (
346 digits[static_cast<std::size_t> (u & 0x0F)]);
347 }
348 else
349 {
350 result.push_back (c);
351 }
352 break;
353 }
354 }
355 result.push_back ('"');
356
357 return result;
358 }

Reference micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_double_quoted.

Referenced by micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_double_quoted and micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_scalar.

yaml_scalar()​

std::string micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_scalar (std::string_view text)

Formats a text as a YAML scalar, double-quoted only when needed.

A text made only of letters, digits, and the characters _, ., +, /, and -, and not starting with -, is a valid plain scalar, whatever its meaning, and is returned unchanged, so that the usual values are written as before. Any other text, for example a file name with a space, a #, or a :, which a YAML parser would truncate, split, or reject as a plain scalar, is returned double-quoted, as yaml_double_quoted() does.

Parameters
text

The text to format.

Returns

The scalar.

Definition at line 377 of file reporter-tap.cpp.

377 yaml_scalar (std::string_view text)
378 {
379 bool is_plain = !text.empty () && text.front () != '-';
380 for (const char c : text)
381 {
382 const bool is_safe = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
383 || (c >= '0' && c <= '9') || c == '_'
384 || c == '.' || c == '+' || c == '/' || c == '-';
385 is_plain = is_plain && is_safe;
386 }
387
388 return is_plain ? std::string{ text } : yaml_double_quoted (text);
389 }

References micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_double_quoted and micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_scalar.

Referenced by micro_os_plus::micro_test_plus::detail::anonymous{reporter-tap.cpp}::yaml_scalar.

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
12// ----------------------------------------------------------------------------
13
32
33// ----------------------------------------------------------------------------
34
39
40#if __has_include("micro-os-plus/diag/trace.h")
41#include "micro-os-plus/diag/trace.h"
42#endif // __has_include("micro-os-plus/diag/trace.h")
43
44#include <string>
45#include <string_view>
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#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
56#endif // defined(__clang__)
57#endif // defined(__GNUC__)
58
59// ============================================================================
60
61#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_ENABLED)
62
63// ----------------------------------------------------------------------------
64
66{
67 // --------------------------------------------------------------------------
68
83 namespace
84 {
103 std::string
104 tap_text (std::string_view text, bool escape)
105 {
106 std::string result;
107 result.reserve (text.size ());
108
109 for (size_t i = 0; i < text.size (); ++i)
110 {
111 const char c = text[i];
112 if (c == '\n' || c == '\r')
113 {
114 // The `\r\n` pair is a single line break.
115 if (c == '\r' && i + 1 < text.size () && text[i + 1] == '\n')
116 {
117 ++i;
118 }
119 result.push_back (' ');
120 }
121 else if (escape && (c == '\\' || c == '#'))
122 {
123 result.push_back ('\\');
124 result.push_back (c);
125 }
126 else
127 {
128 result.push_back (c);
129 }
130 }
131
132 return result;
133 }
134
135#if defined(__GNUC__)
136#pragma GCC diagnostic push
137
138#pragma GCC diagnostic ignored "-Wpadded"
139#endif // defined(__GNUC__)
152 {
153 public:
166 tap_name (const char* text, bool escape) : text_{ text }
167 {
168 // The line breaks are always changed; the other two characters
169 // only when escaping.
170 if (std::string_view{ text }.find_first_of (escape ? "\r\n\\#"
171 : "\r\n")
172 != std::string_view::npos)
173 {
174 prepared_ = tap_text (text, escape);
175 is_copy_ = true;
176 }
177 }
178
186 tap_name (const tap_name&) = delete;
187
195 tap_name (tap_name&&) = delete;
196
205 operator= (const tap_name&)
206 = delete;
207
216 operator= (tap_name&&)
217 = delete;
218
225 ~tap_name () = default;
226
239 [[nodiscard]] const char*
240 c_str (void) const noexcept
241 {
242 return is_copy_ ? prepared_.c_str () : text_;
243 }
244
245 private:
252 const char* text_;
253
260 std::string prepared_{};
261
269 bool is_copy_{ false };
270 };
271#if defined(__GNUC__)
272#pragma GCC diagnostic pop
273#endif // defined(__GNUC__)
274
293 std::string
294 yaml_double_quoted (std::string_view text)
295 {
296 constexpr std::string_view digits{ "0123456789ABCDEF" };
297
298 std::string result;
299 result.reserve (text.size () + 2);
300
301 result.push_back ('"');
302 for (const char c : text)
303 {
304 const auto u = static_cast<unsigned char> (c);
305 switch (c)
306 {
307 case '\\':
308 result.append ("\\\\");
309 break;
310 case '"':
311 result.append ("\\\"");
312 break;
313 case '\0':
314 result.append ("\\0");
315 break;
316 case '\a':
317 result.append ("\\a");
318 break;
319 case '\b':
320 result.append ("\\b");
321 break;
322 case '\t':
323 result.append ("\\t");
324 break;
325 case '\n':
326 result.append ("\\n");
327 break;
328 case '\v':
329 result.append ("\\v");
330 break;
331 case '\f':
332 result.append ("\\f");
333 break;
334 case '\r':
335 result.append ("\\r");
336 break;
337 case '\x1B':
338 result.append ("\\e");
339 break;
340 default:
341 if (u < 0x20 || u == 0x7F)
342 {
343 result.append ("\\x");
344 result.push_back (digits[static_cast<std::size_t> (u >> 4)]);
345 result.push_back (
346 digits[static_cast<std::size_t> (u & 0x0F)]);
347 }
348 else
349 {
350 result.push_back (c);
351 }
352 break;
353 }
354 }
355 result.push_back ('"');
356
357 return result;
358 }
359
376 std::string
377 yaml_scalar (std::string_view text)
378 {
379 bool is_plain = !text.empty () && text.front () != '-';
380 for (const char c : text)
381 {
382 const bool is_safe = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
383 || (c >= '0' && c <= '9') || c == '_'
384 || c == '.' || c == '+' || c == '/' || c == '-';
385 is_plain = is_plain && is_safe;
386 }
387
388 return is_plain ? std::string{ text } : yaml_double_quoted (text);
389 }
390 } // namespace
391
392 // --------------------------------------------------------------------------
393
401 {
402#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
403 trace::printf ("%s\n", __PRETTY_FUNCTION__);
404#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
405 }
406
414 {
415#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
416 trace::printf ("%s\n", __PRETTY_FUNCTION__);
417#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
418 }
419
420 // --------------------------------------------------------------------------
421
432 void
434 {
435#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
436 trace::printf ("%s\n", __PRETTY_FUNCTION__);
437#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
438
439 const bool to_console = route (record_kind::total).console;
440
441 if (to_console)
442 {
443 write_console ("\n");
444 }
445
447
448 write_line (to_console, "TAP version 14\n");
449
451
452 add_empty_line_ = false;
453 }
454
469 void
471 {
472#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
473 trace::printf ("%s\n", __PRETTY_FUNCTION__);
474#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
475
476 char message_summary[32];
477 snprintf (message_summary, sizeof (message_summary),
480
481 char message_totals[totals_size_];
482 format_session_totals (runner, message_totals);
483
484 char message_time[elapsed_time_size_];
486
487 const bool to_console = route (record_kind::total).console;
488
490 {
491 write_console ("\n");
492 }
493
494 write_line (to_console, "%s\n# { total: %s%s }\n", message_summary,
495 message_totals, message_time);
496
498
500 }
501
502 // --------------------------------------------------------------------------
503
513 void
515 {
516#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
517 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
518#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
519
520 const tap_name name{ suite.name (), false };
521
522 const bool to_console = route (record_kind::suite_heading).console;
523
524 if (to_console && add_empty_line_)
525 {
526 write_console ("\n");
527 }
528
529 write_line (to_console, "# Subtest: %s\n", name.c_str ());
530
532
533 if (to_console)
534 {
535 add_empty_line_ = true;
536 }
537
538 enter_suite (suite, to_console);
539 }
540
558 void
560 {
561#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
562 trace::printf (
568 __PRETTY_FUNCTION__, suite.name (), suite.totals ().passed_checks (),
571#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
572
573 std::string indent (indent_size_, ' ');
574
575 // The name is not included, since its length is not bounded; it is
576 // passed to each write.
577 const bool passed = suite.totals ().passed ();
578 const char* result = passed ? "ok" : "not ok";
579 const char* status = passed ? "passed" : "FAILED";
580
581 char message_totals[totals_size_];
582 format_suite_totals (suite, message_totals);
583
584 char message_time[elapsed_time_size_];
585 format_elapsed_time (suite.stopwatch_, message_time);
586
587 const tap_name name{ suite.name (), true };
588
590
591 const bool to_console = route (record_kind::suite_result).console;
592
593 // At this point, the buffer may contain output from the test case, which
594 // should be displayed.
595 if (to_console)
596 {
598 {
599 write_console ("\n");
600 }
601
602 // With verbosity, the full TAP output accumulated in the buffer;
603 // for a failed suite, as it may contain useful information about
604 // the failure.
606 }
607 else
608 {
610 }
611
612 // The plan counts the test cases and the failure of the suite body,
613 // if any, which is numbered after them.
614 write_line (to_console,
617 " - %s # { %s, %s%s }\n",
618 indent.c_str (), suite.test_point_number_, result,
619 suite.own_index_, name.c_str (), status, message_totals,
620 message_time);
621
622 if (!to_console && route (record_kind::total).console)
623 {
624 // Quiet: only the test point of the suite, counted by the session
625 // plan.
627 " - %s # { %s, %s%s }\n",
628 result, suite.own_index_, name.c_str (), status,
629 message_totals, message_time);
630 }
631
633
634 clear_buffer ();
635
636 add_empty_line_ = true;
637
638 current_suite_ = nullptr;
639 }
640
652 void
654 {
655#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
656 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
657#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
658
659 const tap_name name{ suite.name (), true };
660
661 // The skipped suites are counted by the session plan, so they are
662 // displayed with quiet verbosity too.
663 const bool to_console = route (record_kind::total).console;
664
666 {
667 write_console ("\n");
668 }
669
670 write_line (to_console,
672 " - %s # SKIP filtered\n",
673 suite.own_index_, name.c_str ());
674
676
677 add_empty_line_ = true;
678 }
679
690 void
691 reporter_tap::fail_suite (const std::string& message, const suite& suite)
692 {
693#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
694 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, suite.name ());
695#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
696
697 const size_t begin = buffer_.size ();
698
699 *this << indent (1) << "not ok " << suite.test_point_number_ << " - "
700 << tap_text (message, true) << endl;
701
702 mark_failure (begin);
703 }
704
705 // --------------------------------------------------------------------------
706
721 void
723 {
724#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
725 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, test_case.name ());
726#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
727
729
730 const bool to_console = route (record_kind::test_case_heading).console;
731
732 open_test_cases_.push_back ({ &test_case, to_console });
733
734 std::string indent (indent_size_ * test_case.nesting_depth (), ' ');
735
736 const tap_name name{ test_case.name (), false };
737
738 if (to_console && add_empty_line_)
739 {
740 write_console ("\n");
741 }
742
743 write_line (to_console, "%s# Subtest: %s\n", indent.c_str (),
744 name.c_str ());
745
747
748 if (to_console)
749 {
750 add_empty_line_ = false;
751 }
752 }
753
778 void
780 {
781#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
782 trace::printf ("%s '%s' i%" MICRO_OS_PLUS_MICRO_TEST_PLUS_PRI_SIZE
788 __PRETTY_FUNCTION__, test_case.name (),
795#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
796
797 std::string indent (indent_size_ * test_case.nesting_depth (), ' ');
798 std::string indent2 (indent_size_ * (test_case.nesting_depth () + 1), ' ');
799
800 // The nested test cases skipped by the filter keep their index, and are
801 // counted in the plan.
802 size_t plan_count = test_case.totals ().executed_checks ()
805
806 // The name and the indentation are not included, since their lengths
807 // are not bounded; they are passed to each write.
808 const bool passed = test_case.totals ().passed ();
809 const char* result = passed ? "ok" : "not ok";
810 const char* status = passed ? "passed" : "FAILED";
811
812 char message_totals[totals_size_];
813 format_test_case_totals (test_case, message_totals);
814
815 const tap_name name{ test_case.name (), true };
816
818
819 const bool to_console = route (record_kind::test_case_result).console;
820
821 // A passed test case without anything displayed under it is a single
822 // test point.
823 const bool is_single_point
824 = to_console && passed
825 && (open_test_cases_.empty ()
826 || !open_test_cases_.back ().name_displayed);
827
828 if (to_console)
829 {
830 if (is_single_point)
831 {
832 // The headings of the outer test cases, to keep the test point
833 // in their block.
835 }
836 else
837 {
838 // The heading of this test case too, if not displayed yet.
840
842 {
843 write_console ("\n");
844 }
845
846 // The full TAP output accumulated in the buffer, as the
847 // plan counts it.
849
851 "\n",
852 indent2.c_str (), plan_count);
853 }
854 }
855 else
856 {
858 }
859
861 indent2.c_str (), plan_count);
862
863 write_line (to_console,
865 " - %s # { %s, %s }\n",
866 indent.c_str (), result, test_case.own_index_, name.c_str (),
867 status, message_totals);
868
870
871 clear_buffer ();
872
873 add_empty_line_ = true;
874
875 if (!open_test_cases_.empty ())
876 {
877 open_test_cases_.pop_back ();
878 }
879 }
880
881 // --------------------------------------------------------------------------
882
889 const char*
890 reporter_tap::comment_prefix (void) const noexcept
891 {
892 return "# ";
893 }
894
900 const char*
901 reporter_tap::error_prefix (void) const noexcept
902 {
903 return "Bail out! ";
904 }
905
918 void
920 {
922 {
923 return;
924 }
925
926 *this << indent (test_case.nesting_depth () + 1) << "ok "
928 }
929
945 void
946 reporter_tap::output_pass_prefix (const std::string& message,
947 const test_case& test_case)
948 {
949 size_t level = test_case.nesting_depth ();
950 *this << indent (level + 1) << "ok " << test_case.test_point_number_
951 << " - ";
953 if (!message.empty ())
954 {
955 *this << message;
956 }
957 }
958
973 void
975 [[maybe_unused]] const test_case& test_case)
976 {
977 const std::string description = tap_text (
978 std::string_view{ buffer_ }.substr (description_begin_), true);
979 buffer_.replace (description_begin_, std::string::npos, description);
980
981 *this << endl;
982 }
983
997 void
999 const std::string& message, const bool has_expression,
1000 [[maybe_unused]] const reflection::source_location& location,
1001 const test_case& test_case)
1002 {
1003 size_t level = test_case.nesting_depth ();
1004 *this << indent (level + 1) << "not ok " << test_case.test_point_number_;
1005
1006 if (!message.empty ())
1007 {
1008 *this << " - " << tap_text (message, true);
1009 }
1010 *this << endl;
1011
1012 // https://testanything.org/tap-version-14-specification.html
1013 // 2-space indentation for YAML diagnostics.
1014 *this << indent (level + 1) << " ---";
1015 condition_begin_ = std::string::npos;
1016 if (has_expression)
1017 {
1018 *this << endl;
1019 *this << indent (level + 1) << " condition: ";
1020 condition_begin_ = buffer_.size ();
1021 }
1022 }
1023
1056 void
1058 const reflection::source_location& location,
1059 [[maybe_unused]] check_kind kind, const test_case& test_case)
1060 {
1061 if (condition_begin_ != std::string::npos)
1062 {
1063 const std::string condition = yaml_double_quoted (
1064 std::string_view{ buffer_ }.substr (condition_begin_));
1065 buffer_.replace (condition_begin_, std::string::npos, condition);
1066 condition_begin_ = std::string::npos;
1067 }
1068
1069 size_t level = test_case.nesting_depth ();
1070 *this << endl;
1071
1072 // https://testanything.org/tap-version-14-specification.html
1073 // 2-space indentation for YAML diagnostics.
1074
1075 *this << indent (level + 1) << " at:" << endl;
1076 *this << indent (level + 1) << " fileName: "
1077 << yaml_scalar (utility::extract_file_name (location.file_name ()))
1078 << endl;
1079 *this << indent (level + 1) << " lineNumber: " << location.line ()
1080 << endl;
1081
1082 *this << indent (level + 1) << " ..." << endl;
1083 }
1084
1099 void
1101 {
1102#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
1103 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, test_case.name ());
1104#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
1105
1107
1108 std::string indent (indent_size_ * test_case.nesting_depth (), ' ');
1109
1110 const tap_name name{ test_case.name (), true };
1111
1112 const bool to_console = route (record_kind::skipped).console;
1113
1114 if (to_console)
1115 {
1117
1119 {
1120 write_console ("\n");
1121 }
1122 }
1123
1124 write_line (to_console,
1126 " - %s # SKIP filtered\n",
1127 indent.c_str (), test_case.own_index_, name.c_str ());
1128
1129 flush_streams ();
1130
1131 add_empty_line_ = true;
1132 }
1133
1158 void
1160 bool is_requirement)
1161 {
1162#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
1163 trace::printf ("%s\n", __PRETTY_FUNCTION__);
1164#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
1165
1166 // The type of `line()` depends on the toolchain (`uint_least32_t` for
1167 // `std::source_location`); widen it implicitly, without a cast that
1168 // would be useless on some platforms.
1169 const unsigned long line = location.line ();
1170 // The reason is escaped, as the descriptions.
1171 const std::string file_name
1172 = tap_text (utility::extract_file_name (location.file_name ()), true);
1173 const char* reason
1174 = is_requirement ? "Requirement failed" : "Test execution aborted";
1175
1177
1178 const bool to_console = route (record_kind::abort).console;
1179
1180 if (to_console)
1181 {
1182 if (route (record_kind::suite_result).console)
1183 {
1185
1186 if (add_empty_line_)
1187 {
1188 write_console ("\n");
1189 }
1190
1192 }
1193 else
1194 {
1196 }
1197 }
1198
1199 write_line (to_console, "Bail out! %s at %s:%lu\n", reason,
1200 file_name.c_str (), line);
1201
1202 flush_streams ();
1203
1205
1206 clear_buffer ();
1207
1208 // Should the abort handler throw, nothing may refer to the objects
1209 // that are unwound.
1210 open_test_cases_.clear ();
1211 current_suite_ = nullptr;
1212 }
1213
1227 void
1229 {
1231
1233 {
1235
1236 if (add_empty_line_)
1237 {
1238 write_console ("\n");
1239 add_empty_line_ = false;
1240 }
1241
1243 }
1244 else
1245 {
1247 }
1248
1249 flush_streams ();
1250
1251 clear_buffer ();
1252 }
1253
1261 void
1263 {
1264 if (add_empty_line_)
1265 {
1266 write_console ("\n");
1267 }
1268
1269 std::string indent (indent_size_ * open.test->nesting_depth (), ' ');
1270 const tap_name name{ open.test->name (), false };
1271 write_console ("%s# Subtest: %s\n", indent.c_str (), name.c_str ());
1272
1273 add_empty_line_ = false;
1274 }
1275
1285 void
1287 {
1288 if (failures_.empty ())
1289 {
1290 return;
1291 }
1292
1293 std::string path;
1294 if (current_suite_ != nullptr)
1295 {
1296 path = tap_name{ current_suite_->name (), false }.c_str ();
1297 }
1298 for (const open_test_case& open : open_test_cases_)
1299 {
1300 if (!path.empty ())
1301 {
1302 path.append (" > ");
1303 }
1304 path.append (tap_name{ open.test->name (), false }.c_str ());
1305 }
1306 write_console ("%s%s\n", comment_prefix (), path.c_str ());
1307
1308 std::string_view rest{ failures_ };
1309 while (!rest.empty ())
1310 {
1311 const size_t end = rest.find ('\n');
1312 const size_t length
1313 = (end == std::string_view::npos) ? rest.size () : end + 1;
1314 write_console ("%s", comment_prefix ());
1315 write_console_text (rest.substr (0, length));
1316 rest.remove_prefix (length);
1317 }
1318 }
1319
1320 // --------------------------------------------------------------------------
1321} // namespace micro_os_plus::micro_test_plus::detail
1322
1323// ----------------------------------------------------------------------------
1324
1325#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_ENABLED)
1326
1327// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.8 by Doxygen 1.18.0.