micro-test-plus 5.0.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
44
45#if __has_include("micro-os-plus/diag/trace.h")
46#include "micro-os-plus/diag/trace.h"
47#endif // __has_include("micro-os-plus/diag/trace.h")
48
49// ----------------------------------------------------------------------------
50
51#if defined(__GNUC__)
52#pragma GCC diagnostic ignored "-Waggregate-return"
53#if defined(__clang__)
54#pragma clang diagnostic ignored "-Wunknown-warning-option"
55#pragma clang diagnostic ignored "-Wc++98-compat"
56#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
57#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
58#endif // defined(__clang__)
59#endif // defined(__GNUC__)
60
61// =============================================================================
62
63#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_ENABLED)
64
65// ----------------------------------------------------------------------------
66
68{
69 // --------------------------------------------------------------------------
70
80 reporter::reporter (std::unique_ptr<std::vector<std::string_view>> argvs)
81 {
82#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
83 trace::printf ("%s\n", __PRETTY_FUNCTION__);
84#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
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 fprintf (stderr, "error: Failed to open output file '%.*s'\n",
138 static_cast<int> (output_file_sv.size ()),
139 output_file_sv.data ());
140 exit (1);
141 }
142 // The original string is zero terminated, so we can safely use .data()
143 // here.
144 output_file_path_ = output_file_sv.data ();
145 }
146
147 // Pre-allocate buffer to reduce dynamic allocations.
148 buffer_.reserve (128);
149 }
150
159 {
160#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
161 trace::printf ("%s\n", __PRETTY_FUNCTION__);
162#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
163
164 if (output_file_ != nullptr)
165 {
166 fflush (output_file_);
167 fclose (output_file_);
168
169 printf ("Test output written to '%s'.\n", output_file_path_);
170
171 output_file_ = nullptr;
172 output_file_path_ = nullptr;
173 }
174 }
175
176 // --------------------------------------------------------------------------
177
186 reporter&
187 endl (reporter& stream)
188 {
189 stream.endline ();
190 return stream;
191 }
192
201 void
203 {
204 buffer_.append ("\n");
205 flush ();
206 }
207
216 */
217 void
219 {
220 // Pass only the string, do not add an `\n` here.
221 printf ("%s", buffer_.c_str ());
222 }
223
225 * @details
226 * Writes the contents of `buffer_` to `output_file_` using `fprintf`
227 * without appending a newline. If `output_file_` is null, the call is
228 * a no-op.
229 */
230 void
232 {
233 // Pass only the string, do not add an `\n` here.
234 if (output_file_ != nullptr)
235 {
236 fprintf (output_file_, "%s", buffer_.c_str ());
237 }
238 }
239
250 void
252 {
253 if (argvs_ && !argvs_->empty ())
254 {
255 const auto& args = *argvs_;
256 std::string line;
257 line.reserve (256);
258 line.append (get_comment_prefix ());
259 line.append ("Running: ");
260
261 // Append only the file name part of argv[0].
262 const std::string_view arg0 = args[0];
263 const auto sep = arg0.rfind ('/');
264 line.append ((sep != std::string_view::npos) ? arg0.substr (sep + 1)
265 : arg0);
266
267 for (size_t i = 1; i < args.size (); ++i)
268 {
269 line.append (" ");
270 line.append (args[i]);
271 }
272 line.append ("\n");
273
274 if (output_file_ != nullptr)
275 fprintf (output_file_, "%s", line.c_str ());
276
277#if !(defined(MICRO_OS_PLUS_STARTUP_ENABLED) \
278 && defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED))
281 printf ("%s", line.c_str ());
282#endif // !defined(MICRO_OS_PLUS_STARTUP_ENABLED)
284
285 {
286 // Build the "Built with ..." line. For the output file the compiler
287 // version is omitted; for stdout it is appended via __VERSION__.
288 std::string line;
289 line.reserve (256);
290 line.append (get_comment_prefix ());
291 line.append ("Built with ");
292#if defined(__clang__)
293 line.append ("clang " __VERSION__);
294#elif defined(__GNUC__)
295 line.append ("GCC " __VERSION__);
296#elif defined(_MSC_VER)
297 line.append ("MSVC");
298 char msvc_ver[16];
299 snprintf (msvc_ver, sizeof (msvc_ver), " - %d", _MSC_VER);
300 line.append (msvc_ver);
301#else
302 line.append ("an unknown compiler");
303#endif // defined(__clang__)
304#if !(defined(__APPLE__) || defined(__linux__) || defined(__unix__) \
305 || defined(WIN32))
306 // This is relevant only on bare-metal.
307#if defined(__ARM_PCS_VFP) || defined(__ARM_FP)
308 line.append (", with FP");
309#else
310 line.append (", no FP");
311#endif // defined(__ARM_PCS_VFP) || defined(__ARM_FP)
312#endif // !(defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(WIN32))
313#if defined(__EXCEPTIONS)
314 line.append (", with exceptions");
315#else
316 line.append (", no exceptions");
317#endif // defined(__EXCEPTIONS)
318#if defined(MICRO_OS_PLUS_DEBUG_ENABLED)
319 line.append (", with MICRO_OS_PLUS_DEBUG_ENABLED");
320#endif // defined(MICRO_OS_PLUS_DEBUG_ENABLED)
321#if defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED)
322 line.append (", with MICRO_OS_PLUS_DIAG_TRACE_ENABLED");
323#endif // defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED)
324
325 if (output_file_ != nullptr)
326 {
327 fprintf (output_file_, "%s\n", line.c_str ());
328 }
329
330#if !(defined(MICRO_OS_PLUS_STARTUP_ENABLED) \
331 && defined(MICRO_OS_PLUS_DIAG_TRACE_ENABLED))
333 {
334 printf ("%s\n", line.c_str ());
335 }
336#endif // !defined(MICRO_OS_PLUS_STARTUP_ENABLED)
337 }
338 }
339
347 void
349 {
350 fflush (stdout);
351 if (output_file_ != nullptr)
352 {
353 fflush (output_file_);
354 }
355 }
356
357 // --------------------------------------------------------------------------
358
370 {
371 // Call the endl function.
372 (*func) (*this);
373 return *this;
374 }
375
385 reporter::operator<< (std::string_view sv)
386 {
387 buffer_.append (sv);
388 return *this;
389 }
390
400 {
401 buffer_.append (1, c);
402 return *this;
403 }
404
414 reporter::operator<< (const char* s)
415 {
416 buffer_.append (s);
417 return *this;
418 }
419
426 void
427 reporter::pass (std::string& message, const std::string& expression,
429 {
430 output_pass_prefix_ (message, subtest);
431
432 if (message.empty ())
433 {
434 *this << expression;
435 }
436
438 }
439
445 void
446 reporter::fail (bool abort, std::string& message,
447 const std::string& expression, bool has_expression,
448 const reflection::source_location& location,
450 {
451 output_fail_prefix_ (message, has_expression, location, subtest);
452
453 if (has_expression)
454 {
455 *this << expression;
456 }
457
458 output_fail_suffix_ (location, abort, subtest);
459 }
460
461 // --------------------------------------------------------------------------
462} // namespace micro_os_plus::micro_test_plus
463
464// ----------------------------------------------------------------------------
465
466#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_ENABLED)
467
468// ----------------------------------------------------------------------------
Local implementation of source location information for diagnostics.
Definition reflection.h:150
Reporter to display test results, including operand values and types for failures.
Definition reporter.h:195
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:446
reporter(std::unique_ptr< std::vector< std::string_view > > argvs)
Constructor for the reporter class.
Definition reporter.cpp:80
std::unique_ptr< std::vector< std::string_view > > argvs_
Owns the command-line arguments passed to the test runner.
Definition reporter.h:605
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:158
void pass(std::string &message, const std::string &expression, subtest &subtest)
Report a passed condition.
Definition reporter.cpp:427
FILE * output_file_
Optional output file for redirecting test report output.
Definition reporter.h:600
void write_buffer_to_stdout(void)
Output the current buffered content.
Definition reporter.cpp:218
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:561
void write_info_(void)
Appends informational (non-result) text to the output buffer.
Definition reporter.cpp:251
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:590
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:385
void flush(void)
Flush the current buffered content.
Definition reporter.cpp:348
enum verbosity verbosity_
The verbosity level for test reporting.
Definition reporter.h:546
void endline(void)
Inserts a line ending into the output buffer.
Definition reporter.cpp:202
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:551
Primary namespace for the µTest++ testing framework.
reporter & endl(reporter &stream)
Output stream manipulator for ending a line in test reports.
Definition reporter.cpp:187
C++ header file with declarations for the µTest++ test reporter.