Skip to main content

runner-options.cpp File

C++ source file with implementations for the µTest++ runner options. 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...

Description​

C++ source file with implementations for the µTest++ runner options.

This source file contains the command-line parser of the µTest++ framework, implemented by the runner_options constructor taking the main() arguments, and the recording of the errors it finds.

All definitions reside within the micro_os_plus::micro_test_plus namespace.

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

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
29
30// ----------------------------------------------------------------------------
31
33
34#if __has_include("micro-os-plus/diag/trace.h")
35#include "micro-os-plus/diag/trace.h"
36#endif // __has_include("micro-os-plus/diag/trace.h")
37
38#include <string>
39
40// ----------------------------------------------------------------------------
41
42#if defined(__GNUC__)
43#pragma GCC diagnostic ignored "-Waggregate-return"
44#if defined(__clang__)
45#pragma clang diagnostic ignored "-Wunknown-warning-option"
46#pragma clang diagnostic ignored "-Wc++98-compat"
47#pragma clang diagnostic ignored "-Wc++98-c++11-c++14-compat"
48#pragma clang diagnostic ignored "-Wpre-c++17-compat"
49#pragma clang diagnostic ignored "-Wc++20-compat"
50#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
51#endif // defined(__clang__)
52#endif // defined(__GNUC__)
53
54// ============================================================================
55
56#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_ENABLED)
57
58// ----------------------------------------------------------------------------
59
61{
62 // --------------------------------------------------------------------------
63
64#if defined(__GNUC__)
65#pragma GCC diagnostic push
66
67#if defined(__clang__)
68#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
69#endif // defined(__clang__)
70#endif // defined(__GNUC__)
71
119 runner_options::runner_options (int argc, char* argv[])
120 {
121#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
122 trace::printf ("%s\n", __PRETTY_FUNCTION__);
123#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
124
125 if (argc <= 0 || argv == nullptr)
126 {
127 return;
128 }
129
130 // Copy the strings one by one, since the vector assignment uses
131 // std::copy, which triggers -Waggregate-return in the standard library
132 // headers on some targets.
133 arguments_.reserve (static_cast<size_t> (argc));
134 for (int i = 0; i < argc; ++i)
135 {
136 arguments_.emplace_back (argv[i] != nullptr ? argv[i] : "");
137 }
138
139 // Unless the parsing stops earlier, there are no application arguments.
141
142 // What an option with a value sets.
143 enum class setting
144 {
145 set_reporter,
146 set_output_file,
147 set_colour,
148 add_filter
149 };
150
151 // The options with a value, which is given as `--option=<value>`,
152 // or, if `value_may_follow`, also as `--option <value>`. As in the GNU
153 // tools, `--colour` and `--color` alone mean `always`, and the next
154 // argument is never taken as their value.
155#if defined(__GNUC__)
156#pragma GCC diagnostic push
157
158#pragma GCC diagnostic ignored "-Wpadded"
159#endif // defined(__GNUC__)
160
161 struct value_option
162 {
163 std::string_view name;
164 setting what;
165 bool value_may_follow;
166 };
167
168#if defined(__GNUC__)
169#pragma GCC diagnostic pop
170#endif // defined(__GNUC__)
171
172 static constexpr value_option value_options[]
173 = { { "--reporter", setting::set_reporter, true },
174 { "--output-file", setting::set_output_file, true },
175 { "--use-colour", setting::set_colour, true },
176 { "--use-color", setting::set_colour, true },
177 { "--colour", setting::set_colour, false },
178 { "--color", setting::set_colour, false },
179 { "--filter", setting::add_filter, true } };
180
181 // Set the colour mode from the value of `--colour`, `--color`,
182 // `--use-colour`, or `--use-color`; all accept the values and the
183 // synonyms of the GNU tools, which include those of Catch2 and Boost
184 // UT (`yes`, `no`, and `auto`). An unknown mode is reported as an
185 // error.
186 auto set_colour_mode = [&] (std::string_view value)
187 {
188 if (value == "always" || value == "yes" || value == "force")
189 {
191 }
192 else if (value == "never" || value == "no" || value == "none")
193 {
195 }
196 else if (value == "auto" || value == "tty" || value == "if-tty")
197 {
199 }
200 else
201 {
202 add_error ("unknown colour mode '", value, "'");
203 }
204 };
205
206 // Whether an argument is a value of `--colour`, to detect a value
207 // given separately, which the option does not take.
208 auto is_colour_mode = [] (std::string_view value) -> bool
209 {
210 for (const std::string_view mode :
211 { "always", "yes", "force", "never", "no", "none", "auto", "tty",
212 "if-tty" })
213 {
214 if (value == mode)
215 {
216 return true;
217 }
218 }
219 return false;
220 };
221
222 // Apply the value of an option with a value; an invalid value is
223 // reported as an error.
224 auto apply_value = [&] (setting what, std::string_view value)
225 {
226 if (what == setting::set_reporter)
227 {
228 if (value == "human")
229 {
231 }
232 else if (value == "tap")
233 {
235 }
236 else
237 {
238 add_error ("unknown reporter '", value, "'");
239 }
240 }
241 else if (what == setting::set_output_file)
242 {
243 // An empty path disables the output file.
244 output_file_path = value;
245 }
246 else if (what == setting::set_colour)
247 {
248 set_colour_mode (value);
249 }
250 else
251 {
252 filter_patterns.emplace_back (value);
253 }
254 };
255
256 for (size_t i = 1; i < arguments_.size (); ++i)
257 {
258 const std::string_view argument{ arguments_[i] };
259
260 // The application arguments start at the first argument that does
261 // not start with a dash, or after the first `--` alone.
262 if (argument == "--")
263 {
264 application_begin_ = i + 1;
265 break;
266 }
267 if (!argument.starts_with ('-'))
268 {
270 break;
271 }
272
273 if (argument == "--verbose")
274 {
276 continue;
277 }
278 if (argument == "--quiet")
279 {
281 continue;
282 }
283 if (argument == "--silent")
284 {
286 continue;
287 }
288 if (argument == "--list-tests" || argument == "--list")
289 {
290 list_tests = true;
291 continue;
292 }
293 if (argument == "--help" || argument == "-h")
294 {
295 help = true;
296 continue;
297 }
298
299 // Match the options with a value, alone or followed by `=`.
300 const value_option* matched = nullptr;
301 bool has_inline_value = false;
302 std::string_view value{};
303 for (const value_option& option : value_options)
304 {
305 if (argument == option.name)
306 {
307 matched = &option;
308 break;
309 }
310 if (argument.starts_with (option.name)
311 && argument.substr (option.name.size ()).starts_with ('='))
312 {
313 matched = &option;
314 has_inline_value = true;
315 value = argument.substr (option.name.size () + 1);
316 break;
317 }
318 }
319
320 if (matched == nullptr)
321 {
322 // A mistyped option must not silently disable the tests.
323 add_error ("unknown option '", argument, "'");
324 unknown_option_ = true;
325 continue;
326 }
327
328 if (!has_inline_value)
329 {
330 if (!matched->value_may_follow)
331 {
332 // The option alone means `always`. If the next argument
333 // looks like a mode, it was probably meant to be the
334 // value, so report it instead of silently passing it to
335 // the application.
337 if (i + 1 < arguments_.size ()
338 && is_colour_mode (arguments_[i + 1]))
339 {
340 std::string text{ "'" };
341 text.append (argument);
342 text.append ("' takes its value only after '='; write '");
343 text.append (argument);
344 text.append ("=");
345 text.append (arguments_[i + 1]);
346 text.append (
347 "', or put '--' before the application argument");
348 add_error (text);
349 }
350 continue;
351 }
352
353 if (i + 1 >= arguments_.size ())
354 {
355 add_error ("", matched->name, " option requires an argument");
356 continue;
357 }
358 value = arguments_[++i];
359 }
360
361 apply_value (matched->what, value);
362 }
363 }
364
365#if defined(__GNUC__)
366#pragma GCC diagnostic pop
367#endif // defined(__GNUC__)
368
375 void
376 runner_options::add_error (std::string_view before, std::string_view value,
377 std::string_view after)
378 {
379 std::string text{ before };
380 text.append (value);
381 text.append (after);
382
383 errors_.push_back (std::move (text));
384 }
385
386 // --------------------------------------------------------------------------
387} // namespace micro_os_plus::micro_test_plus
388
389// ----------------------------------------------------------------------------
390
391#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_ENABLED)
392
393// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.8 by Doxygen 1.18.0.