micro-test-plus 5.0.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 __has_include(<micro-os-plus/project-config.h>)
44#include <micro-os-plus/project-config.h>
45#elif __has_include(<micro-os-plus/config.h>)
46#pragma message \
47 "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"
48#include <micro-os-plus/config.h>
49#endif // __has_include(<micro-os-plus/project-config.h>)
50
51#if __has_include(<micro-os-plus/micro-test-plus-defines.h>)
52#include <micro-os-plus/micro-test-plus-defines.h>
53#endif // __has_include(<micro-os-plus/micro-test-plus-defines.h>)
54
55#include <micro-os-plus/diag/trace.h>
56
58
59// ----------------------------------------------------------------------------
60
61#if defined(__GNUC__)
62#pragma GCC diagnostic ignored "-Waggregate-return"
63#if defined(__clang__)
64#pragma clang diagnostic ignored "-Wunknown-warning-option"
65#pragma clang diagnostic ignored "-Wc++98-compat"
66#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
67#endif
68#endif
69
70// =============================================================================
71
73{
74 // --------------------------------------------------------------------------
75
85 reporter::reporter (std::unique_ptr<std::vector<std::string_view>> argvs)
86 {
87#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
88 trace::printf ("%s\n", __PRETTY_FUNCTION__);
89#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
90
91 std::string_view output_file_sv{};
92
93 argvs_ = std::move (argvs);
94
95 static constexpr std::string_view output_file_prefix{ "--output-file=" };
96 if (argvs_)
97 {
98 const auto& args = *argvs_;
99 for (size_t i = 0; i < args.size (); ++i)
100 {
101 if (args[i] == "--verbose")
102 {
104 }
105 else if (args[i] == "--quiet")
106 {
108 }
109 else if (args[i] == "--silent")
110 {
112 }
113 else if (args[i].starts_with (output_file_prefix))
114 {
115 output_file_sv = args[i].substr (output_file_prefix.size ());
116 }
117 else if (args[i]
118 == output_file_prefix.substr (
119 0, output_file_prefix.size () - 1))
120 {
121 if (i + 1 < args.size ())
122 {
123 output_file_sv = args[++i];
124 }
125 else
126 {
127 fprintf (stderr, "error: --output-file option requires a "
128 "file path argument\n");
129 exit (1);
130 }
131 }
132 }
133 }
134
135 if (!output_file_sv.empty ())
136 {
137 // .data() is safe: all string_views are views into argv[]
138 // entries, which are null-terminated C strings.
139 output_file_ = fopen (output_file_sv.data (), "w");
140 if (output_file_ == nullptr)
141 {
142#if defined(__GNUC__)
143#pragma GCC diagnostic push
144#if defined(__clang__)
145#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
146#endif
147#endif
148 fprintf (stderr, "error: Failed to open output file '%.*s'\n",
149 static_cast<int> (output_file_sv.size ()),
150 output_file_sv.data ());
151#if defined(__GNUC__)
152#pragma GCC diagnostic pop
153#endif
154 exit (1);
155 }
156 // The original string is zero terminated, so we can safely use .data()
157 // here.
158 output_file_path_ = output_file_sv.data ();
159 }
160
161 // Pre-allocate buffer to reduce dynamic allocations.
162 buffer_.reserve (128);
163 }
164
173 {
174#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
175 trace::printf ("%s\n", __PRETTY_FUNCTION__);
176#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
177
178 if (output_file_ != nullptr)
179 {
180 fflush (output_file_);
181 fclose (output_file_);
182
183#if defined(__GNUC__)
184#pragma GCC diagnostic push
185#if defined(__clang__)
186#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
187#endif
188#endif
189 printf ("Test output written to '%s'.\n", output_file_path_);
190#if defined(__GNUC__)
191#pragma GCC diagnostic pop
192#endif
193
194 output_file_ = nullptr;
195 output_file_path_ = nullptr;
196 }
197 }
198
199 // --------------------------------------------------------------------------
200
207 * all test cases and folders within the µTest++ framework.
208 */
209 reporter&
210 endl (reporter& stream)
211 {
212 stream.endline ();
213 return stream;
214 }
215
216 /**
217 * @details
218 * This method appends a newline character to the internal output buffer of
219 * the `reporter` and immediately flushes the stream. This ensures that
220 * each line of test output is clearly separated and promptly displayed,
221 * enhancing the readability and organisation of test results across all test
222 * cases and folders.
223 */
224 void
226 {
227 buffer_.append ("\n");
228 flush ();
229 }
230
240 void
242 {
243 // Pass only the string, do not add an `\n` here.
244 printf ("%s", buffer_.c_str ());
245 }
246
253 void
255 {
256 // Pass only the string, do not add an `\n` here.
257 if (output_file_ != nullptr)
258 {
259 fprintf (output_file_, "%s", buffer_.c_str ());
260 }
261 }
262
273 void
275 {
276 if (argvs_ && !argvs_->empty ())
277 {
278 const auto& args = *argvs_;
279 std::string line;
280 line.reserve (256);
281 line.append (get_comment_prefix ());
282 line.append ("Running: ");
283
284 // Append only the file name part of argv[0].
285 const std::string_view arg0 = args[0];
286 const auto sep = arg0.rfind ('/');
287 line.append ((sep != std::string_view::npos) ? arg0.substr (sep + 1)
288 : arg0);
289
290 for (size_t i = 1; i < args.size (); ++i)
291 {
292 line.append (" ");
293 line.append (args[i]);
294 }
295 line.append ("\n");
296
297 if (output_file_ != nullptr)
298 fprintf (output_file_, "%s", line.c_str ());
299
300#if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) \
301 && defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED))
304 printf ("%s", line.c_str ());
305#endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
306 }
307
308 {
309 // Build the "Built with ..." line. For the output file the compiler
310 // version is omitted; for stdout it is appended via __VERSION__.
311 std::string line;
312 line.reserve (256);
313 line.append (get_comment_prefix ());
314 line.append ("Built with ");
315#if defined(__clang__)
316 line.append ("clang " __VERSION__);
317#elif defined(__GNUC__)
318 line.append ("GCC " __VERSION__);
319#elif defined(_MSC_VER)
320 line.append ("MSVC");
321 char msvc_ver[16];
322 snprintf (msvc_ver, sizeof (msvc_ver), " - %d", _MSC_VER);
323 line.append (msvc_ver);
324#else
325 line.append ("an unknown compiler");
326#endif
327#if !(defined(__APPLE__) || defined(__linux__) || defined(__unix__) \
328 || defined(WIN32))
329 // This is relevant only on bare-metal.
330#if defined(__ARM_PCS_VFP) || defined(__ARM_FP)
331 line.append (", with FP");
332#else
333 line.append (", no FP");
334#endif
335#endif
336#if defined(__EXCEPTIONS)
337 line.append (", with exceptions");
338#else
339 line.append (", no exceptions");
340#endif
341#if defined(MICRO_OS_PLUS_DEBUG)
342 line.append (", with MICRO_OS_PLUS_DEBUG");
343#endif
344#if defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED)
345 line.append (", with MICRO_OS_PLUS_DIAG_TRACE_ENABLED");
346#endif
347
348 if (output_file_ != nullptr)
349 {
350 fprintf (output_file_, "%s\n", line.c_str ());
351 }
352
353#if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) \
354 && defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED))
356 {
357 printf ("%s\n", line.c_str ());
358 }
359#endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
360 }
361 }
362
370 void
372 {
373 fflush (stdout);
374 if (output_file_ != nullptr)
375 {
376 fflush (output_file_);
377 }
378 }
379
380 // --------------------------------------------------------------------------
381
393 {
394 // Call the endl function.
395 (*func) (*this);
396 return *this;
397 }
398
408 reporter::operator<< (std::string_view sv)
409 {
410 buffer_.append (sv);
411 return *this;
412 }
413
423 {
424 buffer_.append (1, c);
425 return *this;
426 }
427
437 reporter::operator<< (const char* s)
438 {
439 buffer_.append (s);
440 return *this;
441 }
442
449 void
450 reporter::pass (std::string& message, const std::string& expression,
452 {
453 output_pass_prefix_ (message, subtest);
454
455 if (message.empty ())
456 {
457 *this << expression;
458 }
459
461 }
462
468 void
469 reporter::fail (bool abort, std::string& message,
470 const std::string& expression, bool has_expression,
471 const reflection::source_location& location,
473 {
474 output_fail_prefix_ (message, has_expression, location, subtest);
475
476 if (has_expression)
477 {
478 *this << expression;
479 }
480
481 output_fail_suffix_ (location, abort, subtest);
482 }
483
484 // --------------------------------------------------------------------------
485} // namespace micro_os_plus::micro_test_plus
486
487// ----------------------------------------------------------------------------
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:469
reporter(std::unique_ptr< std::vector< std::string_view > > argvs)
Constructor for the reporter class.
Definition reporter.cpp:85
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:172
void pass(std::string &message, const std::string &expression, subtest &subtest)
Report a passed condition.
Definition reporter.cpp:450
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:241
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:274
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:408
void flush(void)
Flush the current buffered content.
Definition reporter.cpp:371
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:225
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:210
C++ header file with declarations for the µTest++ test reporter.