micro-test-plus 4.1.0
µTest++ Testing Framework
Loading...
Searching...
No Matches
reporter.cpp
Go to the documentation of this file.
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
40
41// ----------------------------------------------------------------------------
42
43#if defined(MICRO_OS_PLUS_INCLUDE_CONFIG_H)
44#include <micro-os-plus/config.h>
45#endif // MICRO_OS_PLUS_INCLUDE_CONFIG_H
46
47#if defined(MICRO_OS_PLUS_TRACE)
48#include <micro-os-plus/diag/trace.h>
49#endif // MICRO_OS_PLUS_TRACE
50
52
53// ----------------------------------------------------------------------------
54
55#if defined(__GNUC__)
56#pragma GCC diagnostic ignored "-Waggregate-return"
57#if defined(__clang__)
58#pragma clang diagnostic ignored "-Wunknown-warning-option"
59#pragma clang diagnostic ignored "-Wc++98-compat"
60#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
61#endif
62#endif
63
64// =============================================================================
65
67{
68 // --------------------------------------------------------------------------
69
79 reporter::reporter (std::unique_ptr<std::vector<std::string_view>> argvs)
80 {
81#if defined(MICRO_OS_PLUS_TRACE) \
82 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
83 trace::printf ("%s\n", __PRETTY_FUNCTION__);
84#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
85
86 std::string_view output_file_sv{};
87
88 argvs_ = std::move (argvs);
89
90 static constexpr std::string_view output_file_prefix{ "--output-file=" };
91 if (argvs_)
92 {
93 const auto& args = *argvs_;
94 for (size_t i = 0; i < args.size (); ++i)
95 {
96 if (args[i] == "--verbose")
97 {
99 }
100 else if (args[i] == "--quiet")
101 {
103 }
104 else if (args[i] == "--silent")
105 {
107 }
108 else if (args[i].starts_with (output_file_prefix))
109 {
110 output_file_sv = args[i].substr (output_file_prefix.size ());
111 }
112 else if (args[i]
113 == output_file_prefix.substr (
114 0, output_file_prefix.size () - 1))
115 {
116 if (i + 1 < args.size ())
117 {
118 output_file_sv = args[++i];
119 }
120 else
121 {
122 fprintf (stderr, "error: --output-file option requires a "
123 "file path argument\n");
124 exit (1);
125 }
126 }
127 }
128 }
129
130 if (!output_file_sv.empty ())
131 {
132 // .data() is safe: all string_views are views into argv[]
133 // entries, which are null-terminated C strings.
134 output_file_ = fopen (output_file_sv.data (), "w");
135 if (output_file_ == nullptr)
136 {
137#if defined(__GNUC__)
138#pragma GCC diagnostic push
139#if defined(__clang__)
140#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
141#endif
142#endif
143 fprintf (stderr, "error: Failed to open output file '%.*s'\n",
144 static_cast<int> (output_file_sv.size ()),
145 output_file_sv.data ());
146#if defined(__GNUC__)
147#pragma GCC diagnostic pop
148#endif
149 exit (1);
150 }
151 // The original string is zero terminated, so we can safely use .data()
152 // here.
153 output_file_path_ = output_file_sv.data ();
154 }
155
156 // Pre-allocate buffer to reduce dynamic allocations.
157 buffer_.reserve (128);
158 }
159
168 {
169#if defined(MICRO_OS_PLUS_TRACE) \
170 && defined(MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS)
171 trace::printf ("%s\n", __PRETTY_FUNCTION__);
172#endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS_CONSTRUCTORS
173
174 if (output_file_ != nullptr)
175 {
176 fflush (output_file_);
177 fclose (output_file_);
178
179#if defined(__GNUC__)
180#pragma GCC diagnostic push
181#if defined(__clang__)
182#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
183#endif
184#endif
185 printf ("Test output written to '%s'.\n", output_file_path_);
186#if defined(__GNUC__)
187#pragma GCC diagnostic pop
188#endif
189
190 output_file_ = nullptr;
191 output_file_path_ = nullptr;
192 }
193 }
194
195 // --------------------------------------------------------------------------
196
205 reporter&
206 endl (reporter& stream)
208 stream.endline ();
209 return stream;
210 }
211
216 * each line of test output is clearly separated and promptly displayed,
217 * enhancing the readability and organisation of test results across all test
218 * cases and folders.
219 */
220 void
222 {
223 buffer_.append ("\n");
224 flush ();
226
236 void
238 {
239 // Pass only the string, do not add an `\n` here.
240 printf ("%s", buffer_.c_str ());
241 }
242
249 void
251 {
252 // Pass only the string, do not add an `\n` here.
253 if (output_file_ != nullptr)
254 {
255 fprintf (output_file_, "%s", buffer_.c_str ());
256 }
257 }
258
269 void
271 {
272 if (argvs_ && !argvs_->empty ())
273 {
274 const auto& args = *argvs_;
275 std::string line;
276 line.reserve (256);
277 line.append (get_comment_prefix ());
278 line.append ("Running: ");
279
280 // Append only the file name part of argv[0].
281 const std::string_view arg0 = args[0];
282 const auto sep = arg0.rfind ('/');
283 line.append ((sep != std::string_view::npos) ? arg0.substr (sep + 1)
284 : arg0);
285
286 for (size_t i = 1; i < args.size (); ++i)
287 {
288 line.append (" ");
289 line.append (args[i]);
290 }
291 line.append ("\n");
292
293 if (output_file_ != nullptr)
294 fprintf (output_file_, "%s", line.c_str ());
295
296#if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) && defined(MICRO_OS_PLUS_TRACE))
299 printf ("%s", line.c_str ());
300#endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
301 }
302
303 {
304 // Build the "Built with ..." line. For the output file the compiler
305 // version is omitted; for stdout it is appended via __VERSION__.
306 std::string line;
307 line.reserve (256);
308 line.append (get_comment_prefix ());
309 line.append ("Built with ");
310#if defined(__clang__)
311 line.append ("clang " __VERSION__);
312#elif defined(__GNUC__)
313 line.append ("GCC " __VERSION__);
314#elif defined(_MSC_VER)
315 line.append ("MSVC");
316 char msvc_ver[16];
317 snprintf (msvc_ver, sizeof (msvc_ver), " - %d", _MSC_VER);
318 line.append (msvc_ver);
319#else
320 line.append ("an unknown compiler");
321#endif
322#if !(defined(__APPLE__) || defined(__linux__) || defined(__unix__) \
323 || defined(WIN32))
324 // This is relevant only on bare-metal.
325#if defined(__ARM_PCS_VFP) || defined(__ARM_FP)
326 line.append (", with FP");
327#else
328 line.append (", no FP");
329#endif
330#endif
331#if defined(__EXCEPTIONS)
332 line.append (", with exceptions");
333#else
334 line.append (", no exceptions");
335#endif
336#if defined(MICRO_OS_PLUS_DEBUG)
337 line.append (", with MICRO_OS_PLUS_DEBUG");
338#endif
339#if defined(MICRO_OS_PLUS_TRACE)
340 line.append (", with MICRO_OS_PLUS_TRACE");
341#endif
342
343 if (output_file_ != nullptr)
344 {
345 fprintf (output_file_, "%s\n", line.c_str ());
346 }
347
348#if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) && defined(MICRO_OS_PLUS_TRACE))
350 {
351 printf ("%s\n", line.c_str ());
352 }
353#endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
354 }
355 }
356
364 void
366 {
367 fflush (stdout);
368 if (output_file_ != nullptr)
369 {
370 fflush (output_file_);
371 }
372 }
373
374 // --------------------------------------------------------------------------
375
387 {
388 // Call the endl function.
389 (*func) (*this);
390 return *this;
391 }
392
402 reporter::operator<< (std::string_view sv)
403 {
404 buffer_.append (sv);
405 return *this;
406 }
407
417 {
418 buffer_.append (1, c);
419 return *this;
420 }
421
431 reporter::operator<< (const char* s)
432 {
433 buffer_.append (s);
434 return *this;
435 }
436
443 void
444 reporter::pass (std::string& message, const std::string& expression,
446 {
447 output_pass_prefix_ (message, subtest);
448
449 if (message.empty ())
450 {
451 *this << expression;
452 }
453
455 }
456
462 void
463 reporter::fail (bool abort, std::string& message,
464 const std::string& expression, bool has_expression,
465 const reflection::source_location& location,
467 {
468 output_fail_prefix_ (message, has_expression, location, subtest);
469
470 if (has_expression)
471 {
472 *this << expression;
473 }
474
475 output_fail_suffix_ (location, abort, subtest);
476 }
477
478 // --------------------------------------------------------------------------
479} // namespace micro_os_plus::micro_test_plus
480
481// ----------------------------------------------------------------------------
Local implementation of source location information for diagnostics.
Definition reflection.h:138
virtual const char * get_comment_prefix(void) override
Returns an empty comment prefix string.
Reporter to display test results, including operand values and types for failures.
Definition reporter.h:186
void fail(bool abort, std::string &message, const std::string &expression, bool has_expression, const reflection::source_location &location, subtest &subtest)
Report a failed condition.
Definition reporter.cpp:463
reporter(std::unique_ptr< std::vector< std::string_view > > argvs)
Constructor for the reporter class.
Definition reporter.cpp:79
std::unique_ptr< std::vector< std::string_view > > argvs_
Owns the command-line arguments passed to the test runner.
Definition reporter.h:596
virtual void output_pass_suffix_(subtest &subtest)=0
Outputs the suffix for a passing condition.
virtual ~reporter()
Virtual destructor for the reporter class.
Definition reporter.cpp:167
void pass(std::string &message, const std::string &expression, subtest &subtest)
Report a passed condition.
Definition reporter.cpp:444
FILE * output_file_
Optional output file for redirecting test report output.
Definition reporter.h:591
void write_buffer_to_stdout(void)
Output the current buffered content.
Definition reporter.cpp:237
virtual void output_fail_prefix_(std::string &message, const bool has_expression, const reflection::source_location &location, subtest &subtest)=0
Outputs the prefix for a failing condition.
std::string buffer_
Output accumulation buffer.
Definition reporter.h:552
void write_info_(void)
Appends informational (non-result) text to the output buffer.
Definition reporter.cpp:270
virtual const char * get_comment_prefix(void)=0
Returns the comment-prefix string used by this reporter format.
virtual void output_pass_prefix_(std::string &message, subtest &subtest)=0
Outputs the prefix for a passing condition.
const char * output_file_path_
Optional file path for redirecting test report output.
Definition reporter.h:581
detail::expression_formatter & expression()
Provides access to the expression formatter for this reporter.
reporter & operator<<(std::string_view sv)
Output operator for std::string_view.
Definition reporter.cpp:402
void flush(void)
Flush the current buffered content.
Definition reporter.cpp:365
enum verbosity verbosity_
The verbosity level for test reporting.
Definition reporter.h:537
void endline(void)
Inserts a line ending into the output buffer.
Definition reporter.cpp:221
virtual void output_fail_suffix_(const reflection::source_location &location, bool abort, subtest &subtest)=0
Outputs the suffix for a failing condition.
A named, runnable test case that lives inside a suite.
Definition test.h:542
Primary namespace for the µTest++ testing framework.
reporter & endl(reporter &stream)
Output stream manipulator for ending a line in test reports.
Definition reporter.cpp:206
C++ header file with declarations for the µTest++ test reporter.