micro-test-plus 4.1.1
µ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#if defined(MICRO_OS_PLUS_TRACE)
56#include <micro-os-plus/diag/trace.h>
57#endif // MICRO_OS_PLUS_TRACE
58
60
61// ----------------------------------------------------------------------------
62
63#if defined(__GNUC__)
64#pragma GCC diagnostic ignored "-Waggregate-return"
65#if defined(__clang__)
66#pragma clang diagnostic ignored "-Wunknown-warning-option"
67#pragma clang diagnostic ignored "-Wc++98-compat"
68#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
69#endif
70#endif
71
72// =============================================================================
73
75{
76 // --------------------------------------------------------------------------
77
87 reporter::reporter (std::unique_ptr<std::vector<std::string_view>> argvs)
88 {
89#if defined(MICRO_OS_PLUS_TRACE) \
90 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
91 trace::printf ("%s\n", __PRETTY_FUNCTION__);
92#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
93
94 std::string_view output_file_sv{};
95
96 argvs_ = std::move (argvs);
97
98 static constexpr std::string_view output_file_prefix{ "--output-file=" };
99 if (argvs_)
100 {
101 const auto& args = *argvs_;
102 for (size_t i = 0; i < args.size (); ++i)
103 {
104 if (args[i] == "--verbose")
105 {
107 }
108 else if (args[i] == "--quiet")
109 {
111 }
112 else if (args[i] == "--silent")
113 {
115 }
116 else if (args[i].starts_with (output_file_prefix))
117 {
118 output_file_sv = args[i].substr (output_file_prefix.size ());
119 }
120 else if (args[i]
121 == output_file_prefix.substr (
122 0, output_file_prefix.size () - 1))
123 {
124 if (i + 1 < args.size ())
125 {
126 output_file_sv = args[++i];
127 }
128 else
129 {
130 fprintf (stderr, "error: --output-file option requires a "
131 "file path argument\n");
132 exit (1);
133 }
134 }
135 }
136 }
137
138 if (!output_file_sv.empty ())
139 {
140 // .data() is safe: all string_views are views into argv[]
141 // entries, which are null-terminated C strings.
142 output_file_ = fopen (output_file_sv.data (), "w");
143 if (output_file_ == nullptr)
144 {
145#if defined(__GNUC__)
146#pragma GCC diagnostic push
147#if defined(__clang__)
148#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
149#endif
150#endif
151 fprintf (stderr, "error: Failed to open output file '%.*s'\n",
152 static_cast<int> (output_file_sv.size ()),
153 output_file_sv.data ());
154#if defined(__GNUC__)
155#pragma GCC diagnostic pop
156#endif
157 exit (1);
158 }
159 // The original string is zero terminated, so we can safely use .data()
160 // here.
161 output_file_path_ = output_file_sv.data ();
162 }
163
164 // Pre-allocate buffer to reduce dynamic allocations.
165 buffer_.reserve (128);
166 }
167
176 {
177#if defined(MICRO_OS_PLUS_TRACE) \
178 && defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
179 trace::printf ("%s\n", __PRETTY_FUNCTION__);
180#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
181
182 if (output_file_ != nullptr)
183 {
184 fflush (output_file_);
185 fclose (output_file_);
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 printf ("Test output written to '%s'.\n", output_file_path_);
194#if defined(__GNUC__)
195#pragma GCC diagnostic pop
196#endif
197
198 output_file_ = nullptr;
199 output_file_path_ = nullptr;
200 }
201 }
202
203 // --------------------------------------------------------------------------
204
207 * The `endl` function inserts a newline character into the specified
208 * `reporter` stream and flushes its output buffer. This operation
209 * ensures that each test output line is clearly separated and immediately
210 * visible, facilitating the readability and clarity of test results across
211 * all test cases and folders within the µTest++ framework.
212 */
213 reporter&
214 endl (reporter& stream)
215 {
216 stream.endline ();
217 return stream;
218 }
219
225 * enhancing the readability and organisation of test results across all test
226 * cases and folders.
227 */
228 void
230 {
231 buffer_.append ("\n");
232 flush ();
233 }
234
244 void
246 {
247 // Pass only the string, do not add an `\n` here.
248 printf ("%s", buffer_.c_str ());
249 }
250
257 void
259 {
260 // Pass only the string, do not add an `\n` here.
261 if (output_file_ != nullptr)
262 {
263 fprintf (output_file_, "%s", buffer_.c_str ());
264 }
265 }
266
274 * output file when one is open, and to `stdout` unless verbosity is
275 * set to `silent`.
276 */
277 void
279 {
280 if (argvs_ && !argvs_->empty ())
281 {
282 const auto& args = *argvs_;
283 std::string line;
284 line.reserve (256);
285 line.append (get_comment_prefix ());
286 line.append ("Running: ");
287
288 // Append only the file name part of argv[0].
289 const std::string_view arg0 = args[0];
290 const auto sep = arg0.rfind ('/');
291 line.append ((sep != std::string_view::npos) ? arg0.substr (sep + 1)
292 : arg0);
293
294 for (size_t i = 1; i < args.size (); ++i)
295 {
296 line.append (" ");
297 line.append (args[i]);
298 }
299 line.append ("\n");
300
301 if (output_file_ != nullptr)
302 fprintf (output_file_, "%s", line.c_str ());
303
304#if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) && defined(MICRO_OS_PLUS_TRACE))
307 printf ("%s", line.c_str ());
308#endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
309 }
310
311 {
312 // Build the "Built with ..." line. For the output file the compiler
313 // version is omitted; for stdout it is appended via __VERSION__.
314 std::string line;
315 line.reserve (256);
316 line.append (get_comment_prefix ());
317 line.append ("Built with ");
318#if defined(__clang__)
319 line.append ("clang " __VERSION__);
320#elif defined(__GNUC__)
321 line.append ("GCC " __VERSION__);
322#elif defined(_MSC_VER)
323 line.append ("MSVC");
324 char msvc_ver[16];
325 snprintf (msvc_ver, sizeof (msvc_ver), " - %d", _MSC_VER);
326 line.append (msvc_ver);
327#else
328 line.append ("an unknown compiler");
329#endif
330#if !(defined(__APPLE__) || defined(__linux__) || defined(__unix__) \
331 || defined(WIN32))
332 // This is relevant only on bare-metal.
333#if defined(__ARM_PCS_VFP) || defined(__ARM_FP)
334 line.append (", with FP");
335#else
336 line.append (", no FP");
337#endif
338#endif
339#if defined(__EXCEPTIONS)
340 line.append (", with exceptions");
341#else
342 line.append (", no exceptions");
343#endif
344#if defined(MICRO_OS_PLUS_DEBUG)
345 line.append (", with MICRO_OS_PLUS_DEBUG");
346#endif
347#if defined(MICRO_OS_PLUS_TRACE)
348 line.append (", with MICRO_OS_PLUS_TRACE");
349#endif
350
351 if (output_file_ != nullptr)
352 {
353 fprintf (output_file_, "%s\n", line.c_str ());
354 }
355
356#if !(defined(MICRO_OS_PLUS_INCLUDE_STARTUP) && defined(MICRO_OS_PLUS_TRACE))
358 {
359 printf ("%s\n", line.c_str ());
360 }
361#endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
362 }
363 }
364
372 void
374 {
375 fflush (stdout);
376 if (output_file_ != nullptr)
377 {
378 fflush (output_file_);
379 }
380 }
381
382 // --------------------------------------------------------------------------
383
395 {
396 // Call the endl function.
397 (*func) (*this);
398 return *this;
399 }
400
410 reporter::operator<< (std::string_view sv)
411 {
412 buffer_.append (sv);
413 return *this;
414 }
415
425 {
426 buffer_.append (1, c);
427 return *this;
428 }
429
439 reporter::operator<< (const char* s)
440 {
441 buffer_.append (s);
442 return *this;
443 }
444
451 void
452 reporter::pass (std::string& message, const std::string& expression,
454 {
455 output_pass_prefix_ (message, subtest);
456
457 if (message.empty ())
458 {
459 *this << expression;
460 }
461
463 }
464
470 void
471 reporter::fail (bool abort, std::string& message,
472 const std::string& expression, bool has_expression,
473 const reflection::source_location& location,
475 {
476 output_fail_prefix_ (message, has_expression, location, subtest);
477
478 if (has_expression)
479 {
480 *this << expression;
481 }
482
483 output_fail_suffix_ (location, abort, subtest);
484 }
485
486 // --------------------------------------------------------------------------
487} // namespace micro_os_plus::micro_test_plus
488
489// ----------------------------------------------------------------------------
Local implementation of source location information for diagnostics.
Definition reflection.h:138
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:471
reporter(std::unique_ptr< std::vector< std::string_view > > argvs)
Constructor for the reporter class.
Definition reporter.cpp:87
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:175
void pass(std::string &message, const std::string &expression, subtest &subtest)
Report a passed condition.
Definition reporter.cpp:452
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:245
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:278
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:410
void flush(void)
Flush the current buffered content.
Definition reporter.cpp:373
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:229
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:214
C++ header file with declarations for the µTest++ test reporter.