Skip to main content

test-inlines.h File

C++ header file with inline implementations for the µTest++ test hierarchy. More...

Included Headers​

#include "micro-os-plus/micro-test-plus/check-result.h" #include "micro-os-plus/micro-test-plus/detail.h" #include "micro-os-plus/micro-test-plus/reporter.h" #include <cstdio> #include <cstring> #include <exception>

Namespaces Index​

namespacemicro_os_plus

The primary namespace for the µOS++ framework. More...

namespacemicro_test_plus

Primary namespace for the µTest++ testing framework. More...

namespacedetail

Internal implementation details for the µTest++ framework. More...

Functions Index​

template <typename Self_T, typename Callable_T, typename... Args_T>
autobind_callable (Callable_T &&callable, Args_T &&... arguments)

Creates the function object that holds the body of a test case or suite. More...

template <typename Callable_T, typename... Args_T>
constexpr voidreject_suite_body (void) noexcept

Stops the compilation with a message that tells what the body of a suite takes. More...

template <typename Callable_T, typename... Args_T>
constexpr voidreject_test_body (void) noexcept

Stops the compilation with a message that tells what the body of a test case takes. More...

template <class Self_T, class Values_T, class Callable_T>
voidtest_each_value (Self_T &self, const located_name &name, Values_T &&values, Callable_T &callable)

Adds and runs a test case for each value. More...

Description​

C++ header file with inline implementations for the µTest++ test hierarchy.

This header provides the inline and template implementations for the test hierarchy used within the µTest++ framework: the accessors of the base classes, the constructors that bind the callable objects and their arguments, the test() member functions that create and run test cases, and the expect() and require() checks.

When additional arguments are given, a detail::bound_callable holds copies of the callable and of the arguments. The suites store this object in a std::function, while the test cases only refer to it, through a detail::callable_ref, as a local object of the test() call that runs them. A static_suite additionally inserts itself into the list of suites upon construction.

All definitions reside within the micro_os_plus::micro_test_plus namespace.

This file is included by test.h; the applications include micro-os-plus/micro-test-plus.h, not this file.

Functions​

bind_callable()​

template <typename Self_T, typename Callable_T, typename... Args_T>
auto micro_os_plus::micro_test_plus::detail::bind_callable (Callable_T && callable, Args_T &&... arguments)
nodiscard

Creates the function object that holds the body of a test case or suite.

Without additional arguments, the callable already has the signature void (Self_T&), and a copy of it is returned, without a wrapper.

Otherwise, a bound_callable stores copies of the callable and of the arguments (decayed, as by auto), since a suite may run after the caller's temporaries are gone; the callable receives the copies as non-const lvalues. A reference parameter thus refers to the copy; to refer to the caller's object, pass a pointer, or std::ref(), which converts to a T& parameter (an auto& parameter receives the std::reference_wrapper itself).

The returned object holds a copy of the callable (decayed, as by auto) when there are no additional arguments; otherwise it is a bound_callable that holds copies of the callable and of the arguments, and passes the arguments after the leading reference, which is passed when the body is invoked. No memory is allocated, other than by the copies themselves.

A suite stores the object in a std::function, since it runs later; a test case, which runs immediately, refers to it with a callable_ref.

Template Parameters
Self_T

The type referred to by the first parameter of the callable (test_case or suite).

Callable_T

The type of the callable object.

Args_T

The types of the additional callable arguments.

Parameters
callable

A generic callable object, usually a lambda.

arguments

A possibly empty list of arguments forwarded to the callable after the leading Self_T& reference.

Returns

The function object, invocable with a Self_T& argument.

Definition at line 344 of file test-inlines.h.

344 bind_callable (Callable_T&& callable, Args_T&&... arguments)
345 {
346 if constexpr (sizeof...(arguments) == 0)
347 {
348 return std::forward<Callable_T> (callable);
349 }
350 else
351 {
353 std::decay_t<Args_T>...>{
354 std::forward<Callable_T> (callable),
355 std::forward<Args_T> (arguments)...
356 };
357 }
358 }

Reference micro_os_plus::micro_test_plus::detail::bind_callable.

Referenced by micro_os_plus::micro_test_plus::detail::bind_callable and micro_os_plus::micro_test_plus::detail::runnable_base::create_test_case.

reject_suite_body()​

template <typename Callable_T, typename... Args_T>
void micro_os_plus::micro_test_plus::detail::reject_suite_body (void)
constexpr noexcept

Stops the compilation with a message that tells what the body of a suite takes.

Always fails the static assertion that applies, since it is called only when suite_body is not satisfied. A body that cannot be copied, or a body written for a test case, is recognised, to say so.

Called by the overloads of runner::add_suite() and of the static_suite constructor that are selected when the callable is not a valid body, as reject_test_body() is for the test cases.

Template Parameters
Callable_T

The type of the callable.

Args_T

The types of the additional arguments.

Parameters

None.

Returns

Nothing.

Definition at line 448 of file test-inlines.h.

448 reject_suite_body (void) noexcept
449 {
450 if constexpr (not std::is_copy_constructible_v<std::decay_t<Callable_T>>
451 or not(std::is_copy_constructible_v<std::decay_t<Args_T>>
452 and ...))
453 {
454 static_assert (dependent_false_v<Callable_T, Args_T...>,
455 "the body of a suite and its additional arguments "
456 "must be copyable, since the suite runs later");
457 }
458 else if constexpr (std::is_invocable_v<std::decay_t<Callable_T>&,
459 test_case&,
460 std::decay_t<Args_T>&...>)
461 {
462 static_assert (dependent_false_v<Callable_T, Args_T...>,
463 "the body of a suite takes a suite& as its first "
464 "parameter, not a test_case&");
465 }
466 else
467 {
468 static_assert (dependent_false_v<Callable_T, Args_T...>,
469 "the body of a suite takes a suite& as its first "
470 "parameter, followed by the additional arguments");
471 }
472 }

References micro_os_plus::micro_test_plus::detail::dependent_false_v and micro_os_plus::micro_test_plus::detail::reject_suite_body.

Referenced by micro_os_plus::micro_test_plus::runner::add_suite and micro_os_plus::micro_test_plus::detail::reject_suite_body.

reject_test_body()​

template <typename Callable_T, typename... Args_T>
void micro_os_plus::micro_test_plus::detail::reject_test_body (void)
constexpr noexcept

Stops the compilation with a message that tells what the body of a test case takes.

Always fails the static assertion that applies, since it is called only when test_body is not satisfied. A body written for a suite is recognised, to say so.

Called by the overloads of test_case::test() and suite::test() that are selected when the callable is not a valid body, so that the diagnostic names the mistake in the terms of the application, instead of the concepts and the instantiation stack. Never called at run time.

Template Parameters
Callable_T

The type of the callable.

Args_T

The types of the additional arguments.

Parameters

None.

Returns

Nothing.

Definition at line 422 of file test-inlines.h.

422 reject_test_body (void) noexcept
423 {
424 if constexpr (std::is_invocable_v<std::decay_t<Callable_T>&, suite&,
425 std::decay_t<Args_T>&...>)
426 {
427 static_assert (dependent_false_v<Callable_T, Args_T...>,
428 "the body of a test case takes a test_case& as its "
429 "first parameter, not a suite&");
430 }
431 else
432 {
433 static_assert (dependent_false_v<Callable_T, Args_T...>,
434 "the body of a test case takes a test_case& as its "
435 "first parameter, followed by the additional "
436 "arguments passed to test ()");
437 }
438 }

References micro_os_plus::micro_test_plus::detail::dependent_false_v and micro_os_plus::micro_test_plus::detail::reject_test_body.

Referenced by micro_os_plus::micro_test_plus::detail::reject_test_body.

test_each_value()​

template <class Self_T, class Values_T, class Callable_T>
void micro_os_plus::micro_test_plus::detail::test_each_value (Self_T & self, const located_name & name, Values_T && values, Callable_T & callable)

Adds and runs a test case for each value.

For each value, the name is built in a local string, which lives while the test case runs, since a test case runs within the call to test(), and the test case is added with test(), with the location of the call to test_each(). The callable receives each value as a const lvalue, referring to the element itself; neither the values nor the callable are copied. The values of a range are iterated by a range-based for statement, and those of a tuple are expanded by std::apply(), in order.

The common implementation of test_case::test_each() and suite::test_each().

Template Parameters
Self_T

The type of the test case or suite.

Values_T

The type of the range or tuple of values.

Callable_T

The type of the callable object.

Parameters
self

The test case or suite to which the test cases are added.

name

The common name of the test cases.

values

The range or tuple of values.

callable

The callable object, invoked with the test case and each value.

Returns

Nothing.

Definition at line 1029 of file test-inlines.h.

1029 test_each_value (Self_T& self, const located_name& name, Values_T&& values,
1030 Callable_T& callable)
1031 {
1032 std::size_t position = 0;
1033
1034 const auto run_one
1035 = [&self, &name, &callable, &position] (const auto& value)
1036 {
1037 ++position;
1038
1039 std::string text{ name.name () };
1040 text += " (";
1041 if constexpr (extension::printable<
1042 std::remove_cvref_t<decltype (value)>>)
1043 {
1045 }
1046 else
1047 {
1048 text += '#';
1049 plain_text::append (text, position);
1050 }
1051 text += ')';
1052
1053 // The test case runs within this call, so the value and the
1054 // callable are captured by reference, not copied.
1055 self.test (located_name{ std::move (text), name.location () },
1056 [&callable, &value] (auto& test_self)
1057 {
1058 if constexpr (std::is_member_pointer_v<
1059 std::remove_cvref_t<Callable_T>>)
1060 {
1061 std::invoke (callable, test_self, value);
1062 }
1063 else
1064 {
1065 callable (test_self, value);
1066 }
1067 });
1068 };
1069
1070 if constexpr (test_values_range<Values_T>)
1071 {
1072 for (const auto& value : values)
1073 {
1074 run_one (value);
1075 }
1076 }
1077 else
1078 {
1079 std::apply ([&run_one] (const auto&... elements)
1080 { (run_one (elements), ...); }, values);
1081 }
1082 }

References micro_os_plus::micro_test_plus::detail::plain_text::append, micro_os_plus::micro_test_plus::located_name::location, micro_os_plus::micro_test_plus::located_name::name and micro_os_plus::micro_test_plus::detail::test_each_value.

Referenced by micro_os_plus::micro_test_plus::suite::test_each, micro_os_plus::micro_test_plus::suite::test_each, micro_os_plus::micro_test_plus::test_case::test_each, micro_os_plus::micro_test_plus::test_case::test_each and micro_os_plus::micro_test_plus::detail::test_each_value.

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 * 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
43
44#ifndef MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_TEST_INLINES_H_
45#define MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_TEST_INLINES_H_
46
47// ----------------------------------------------------------------------------
48
49#if defined(__cplusplus)
50
51// ----------------------------------------------------------------------------
52
56
57#if __has_include("micro-os-plus/diag/trace.h")
58#include "micro-os-plus/diag/trace.h"
59#endif // __has_include("micro-os-plus/diag/trace.h")
60
61#include <cstdio>
62#include <cstring>
63#include <exception>
64
65// ----------------------------------------------------------------------------
66
67#if defined(__GNUC__)
68#pragma GCC diagnostic push
69
70#pragma GCC diagnostic ignored "-Waggregate-return"
71#pragma GCC diagnostic ignored "-Wformat"
72#pragma GCC diagnostic ignored "-Wformat-extra-args"
73#pragma GCC diagnostic ignored "-Wpadded"
74#if defined(__clang__)
75#pragma clang diagnostic ignored "-Wunknown-warning-option"
76#pragma clang diagnostic ignored "-Wc++98-compat"
77#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
78#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
79#else // GCC only
80#pragma GCC diagnostic ignored "-Wredundant-tags"
81#endif // defined(__clang__)
82#endif // defined(__GNUC__)
83
84// ============================================================================
85
87{
88 // ==========================================================================
89
97 template <typename Pointer_T>
98 requires (std::is_same_v<Pointer_T, const char*>
99 or std::is_same_v<Pointer_T, char*>
100 or std::is_null_pointer_v<Pointer_T>)
103 : name_{ name != nullptr ? name : "" }, location_{ location }
104 {
106 }
107
115 template <std::size_t N>
118 : name_{ std::string_view{ name, N }.substr (
119 0, std::string_view{ name, N }.find ('\0')) },
121 {
123 }
124
132 const std::string& name, const reflection::source_location& location)
134 {
136 }
137
145 std::string&& name, const reflection::source_location& location)
146 : name_{ std::move (name) }, location_{ location }
147 {
149 }
150
158 std::string_view name, const reflection::source_location& location)
160 {
162 }
163
168 inline const char*
169 located_name::name (void) const noexcept
170 {
171 return name_.c_str ();
172 }
173
178 inline const reflection::source_location&
179 located_name::location (void) const noexcept
180 {
181 return location_;
182 }
183
184 // --------------------------------------------------------------------------
185
186 namespace detail
187 {
188 // ========================================================================
189
194 inline const char*
195 runnable_base::name (void) const noexcept
196 {
197 return name_;
198 }
199
204 inline const counters&
205 runnable_base::totals (void) const noexcept
206 {
207 return totals_;
208 }
209
210#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
216 // Deprecated since 6.0.0; internal to the framework.
217 inline size_t
218 runnable_base::own_index (void) const noexcept
219 {
220 return own_index_;
221 }
222
227 // Deprecated since 6.0.0; internal to the framework.
228 inline size_t
230 {
231 return test_point_number_;
232 }
233
239 // Deprecated since 6.0.0; internal to the framework.
240 inline size_t
242 {
244 }
245#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
246
254 inline size_t
256 {
257 return ++test_point_number_;
258 }
259
264 inline runner&
265 runnable_base::test_runner (void) const noexcept
266 {
267 return runner_;
268 }
269
290 template <typename Callable_T, typename... Args_T>
291 inline void
293 suite& parent_suite, size_t nesting_depth,
294 Callable_T&& callable,
295 Args_T&&... arguments)
296 {
298
299 if constexpr (sizeof...(arguments) == 0
300 && !std::is_function_v<
301 std::remove_reference_t<Callable_T>>)
302 {
303 // The test case runs and ends within this call, so it refers
304 // to the callable of the caller, which is not copied, and is
305 // called in place.
306 class test_case child_test_case{
307 name, runner_, parent_suite,
308 own_index, nesting_depth, callable_ref<class test_case>{ callable }
309 };
310
311 after_test_case_create (child_test_case, parent_suite);
312 }
313 else
314 {
316 std::forward<Callable_T> (callable),
317 std::forward<Args_T> (arguments)...);
318 class test_case child_test_case{
319 name, runner_, parent_suite,
320 own_index, nesting_depth, callable_ref<class test_case>{ body }
321 };
322
323 after_test_case_create (child_test_case, parent_suite);
324 }
325 }
326
327 // ========================================================================
328
342 template <typename Self_T, typename Callable_T, typename... Args_T>
343 auto
344 bind_callable (Callable_T&& callable, Args_T&&... arguments)
345 {
346 if constexpr (sizeof...(arguments) == 0)
347 {
348 return std::forward<Callable_T> (callable);
349 }
350 else
351 {
353 std::decay_t<Args_T>...>{
354 std::forward<Callable_T> (callable),
355 std::forward<Args_T> (arguments)...
356 };
357 }
358 }
359
360 // ========================================================================
361
367 template <typename Self_T, typename Callable_T, typename... Args_T>
368 template <typename C, typename... A>
369 requires (sizeof...(A) > 0)
371 C&& callable, A&&... arguments)
372 : callable_ (std::forward<C> (callable)),
373 arguments_ (std::forward<A> (arguments)...)
374 {
375 }
376
381 template <typename Self_T, typename Callable_T, typename... Args_T>
382 inline void
384 {
385 invoke (self, std::index_sequence_for<Args_T...>{});
386 }
387
396 template <typename Self_T, typename Callable_T, typename... Args_T>
397 template <std::size_t... Is>
398 inline void
400 Self_T& self, std::index_sequence<Is...>)
401 {
402 if constexpr (std::is_member_pointer_v<Callable_T>)
403 {
404 std::invoke (callable_, self, std::get<Is> (arguments_)...);
405 }
406 else
407 {
408 callable_ (self, std::get<Is> (arguments_)...);
409 }
410 }
411
412 // ========================================================================
413
420 template <typename Callable_T, typename... Args_T>
421 constexpr void
422 reject_test_body (void) noexcept
423 {
424 if constexpr (std::is_invocable_v<std::decay_t<Callable_T>&, suite&,
425 std::decay_t<Args_T>&...>)
426 {
427 static_assert (dependent_false_v<Callable_T, Args_T...>,
428 "the body of a test case takes a test_case& as its "
429 "first parameter, not a suite&");
430 }
431 else
432 {
433 static_assert (dependent_false_v<Callable_T, Args_T...>,
434 "the body of a test case takes a test_case& as its "
435 "first parameter, followed by the additional "
436 "arguments passed to test ()");
437 }
438 }
439
446 template <typename Callable_T, typename... Args_T>
447 constexpr void
448 reject_suite_body (void) noexcept
449 {
450 if constexpr (not std::is_copy_constructible_v<std::decay_t<Callable_T>>
451 or not(std::is_copy_constructible_v<std::decay_t<Args_T>>
452 and ...))
453 {
454 static_assert (dependent_false_v<Callable_T, Args_T...>,
455 "the body of a suite and its additional arguments "
456 "must be copyable, since the suite runs later");
457 }
458 else if constexpr (std::is_invocable_v<std::decay_t<Callable_T>&,
459 test_case&,
460 std::decay_t<Args_T>&...>)
461 {
462 static_assert (dependent_false_v<Callable_T, Args_T...>,
463 "the body of a suite takes a suite& as its first "
464 "parameter, not a test_case&");
465 }
466 else
467 {
468 static_assert (dependent_false_v<Callable_T, Args_T...>,
469 "the body of a suite takes a suite& as its first "
470 "parameter, followed by the additional arguments");
471 }
472 }
473
479 template <typename Self_T>
480 template <typename Callable_T>
481 requires (!std::is_same_v<std::remove_cv_t<Callable_T>,
482 callable_ref<Self_T>>)
483 callable_ref<Self_T>::callable_ref (Callable_T& callable) noexcept
484 : callable_{ const_cast<void*> (
485 static_cast<const volatile void*> (std::addressof (callable))) },
486 invoke_{ &invoke_callable<Callable_T> }
487 {
488 }
489
494 template <typename Self_T>
495 inline void
497 {
498 invoke_ (callable_, self);
499 }
500
512 template <typename Self_T>
513 template <typename Callable_T>
514 void
515 callable_ref<Self_T>::invoke_callable (void* callable, Self_T& self)
516 {
517 if constexpr (std::is_member_pointer_v<Callable_T>)
518 {
519 std::invoke (*static_cast<Callable_T*> (callable), self);
520 }
521 else
522 {
523 (*static_cast<Callable_T*> (callable)) (self);
524 }
525 }
526
527 // ------------------------------------------------------------------------
528 } // namespace detail
529
530 // ==========================================================================
531
540 suite& parent_suite, size_t own_index,
541 size_t nesting_depth,
544 callable_{ body }, located_name_{ name },
546 {
547#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
548 trace::printf ("%s '%s' %" MICRO_OS_PLUS_MICRO_TEST_PLUS_PRI_SIZE
550 __PRETTY_FUNCTION__, name_, own_index_, nesting_depth_);
551#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
552 }
553
561 template <typename Callable_T, typename... Args_T>
562 requires detail::test_body<Callable_T, Args_T...>
563 void
564 test_case::test (const located_name& name, Callable_T&& callable,
565 Args_T&&... arguments)
566 {
567#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
568 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name.name ());
569#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
570
571 if (is_misdirected ()) [[unlikely]]
572 {
574 return;
575 }
576
578 std::forward<Callable_T> (callable),
579 std::forward<Args_T> (arguments)...);
580 }
581
582#if !defined(__DOXYGEN__)
588 template <typename Callable_T, typename... Args_T>
589 requires (!detail::test_body<Callable_T, Args_T...>)
590 void
591 test_case::test ([[maybe_unused]] const located_name& name,
592 [[maybe_unused]] Callable_T&& callable,
593 [[maybe_unused]] Args_T&&... arguments)
594 {
595 detail::reject_test_body<Callable_T, Args_T...> ();
596 }
597#endif // !defined(__DOXYGEN__)
598
604 inline bool
605 test_case::is_misdirected (void) const noexcept
606 {
607 const test_case* running = running_test_case ();
608 return running != nullptr && running != this;
609 }
610
620 template <class Expr_T>
623 test_case::expect (const Expr_T& expr, const reflection::source_location& sl,
624 detail::check_result&& result)
625 {
626 if (is_misdirected ()) [[unlikely]]
627 {
629 std::move (result));
630 }
631
632 result.init (expr, detail::check_kind::expectation, sl, *this,
633 test_reporter ().expression_,
634 test_reporter ().writes_passed_checks ());
635 return std::move (result);
636 }
637
638#if !defined(__DOXYGEN__)
645 template <class Expr_T>
648 test_case::expect ([[maybe_unused]] const Expr_T& expr,
649 [[maybe_unused]] const reflection::source_location& sl,
650 detail::check_result&& result)
651 {
653 {
654 static_assert (not detail::ambiguous_condition<Expr_T>,
655 "expect() does not take a wrapper of a bool, whose "
656 "condition tests the wrapper, not the value; write "
657 "has_value () or != nullptr, or use eq ()");
658 }
659 else
660 {
661 static_assert (extension::checkable<Expr_T>,
662 "expect() takes a Boolean condition or a comparison, "
663 "such as eq (); a value of this type is neither");
664 }
665 return std::move (result);
666 }
667#endif // !defined(__DOXYGEN__)
668
680 template <class Expr_T>
683 test_case::require (const Expr_T& expr,
685 detail::check_result&& result)
686 {
687 if (is_misdirected ()) [[unlikely]]
688 {
690 std::move (result));
691 }
692
693 result.init (expr, detail::check_kind::requirement, sl, *this,
694 test_reporter ().expression_,
695 test_reporter ().writes_passed_checks ());
696 return std::move (result);
697 }
698
699#if !defined(__DOXYGEN__)
707 template <class Expr_T>
710 test_case::require ([[maybe_unused]] const Expr_T& expr,
711 [[maybe_unused]] const reflection::source_location& sl,
712 detail::check_result&& result)
713 {
715 {
716 static_assert (not detail::ambiguous_condition<Expr_T>,
717 "require() does not take a wrapper of a bool, whose "
718 "condition tests the wrapper, not the value; write "
719 "has_value () or != nullptr, or use eq ()");
720 }
721 else
722 {
723 static_assert (extension::checkable<Expr_T>,
724 "require() takes a Boolean condition or a comparison, "
725 "such as eq (); a value of this type is neither");
726 }
727 return std::move (result);
728 }
729#endif // !defined(__DOXYGEN__)
730
731#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
732#if !defined(__DOXYGEN__)
733 // Hidden from Doxygen: the comments of these definitions would be
734 // merged into the documentation of `expect()` and `require()`, which
735 // differ only in their `requires` clause; the comments of the
736 // declarations are the documentation.
742 template <class Expr_T>
745 test_case::expect (const Expr_T& expr, const reflection::source_location& sl,
746 detail::check_result&& result)
747 {
748 result.init (expr, detail::check_kind::expectation, sl, *this,
749 test_reporter ().expression_,
750 test_reporter ().writes_passed_checks ());
751 return std::move (result);
752 }
753
759 template <class Expr_T>
762 test_case::require (const Expr_T& expr,
764 detail::check_result&& result)
765 {
766 result.init (expr, detail::check_kind::requirement, sl, *this,
767 test_reporter ().expression_,
768 test_reporter ().writes_passed_checks ());
769 return std::move (result);
770 }
771#endif // !defined(__DOXYGEN__)
772
779 // Deprecated since 6.0.0; use require().
780 template <class Expr_T>
783 test_case::assume (const Expr_T& expr, const reflection::source_location& sl,
784 detail::check_result&& result)
785 {
786 return require (expr, sl, std::move (result));
787 }
788#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
789
795 inline size_t
796 test_case::nesting_depth (void) const noexcept
797 {
798 return nesting_depth_;
799 }
800
826 inline void
828 {
829#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
830 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
831#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
832
833 detail::reporter& reporter = this->test_reporter ();
834
835 // For now, test cases do not record the time.
836 // this->stopwatch_.start ();
837 reporter.begin_test_case (*this);
838
839#if defined(__cpp_exceptions)
840 try
841 {
842 // Invoke the callable, passing the self reference; the arguments
843 // of test() are bound to it, by bind_callable().
844 callable_ (*this);
845 }
846 catch (const detail::failed_requirement&)
847 {
848 // A failed require() stopped the test case; already reported.
849 }
850 catch (const std::exception& e)
851 {
852 if (is_aborted ())
853 {
854 // Thrown by the abort handler; the report is complete.
855 throw;
856 }
857 fail_unexpected_exception (e.what ());
858 }
859 catch (...)
860 {
861 if (is_aborted ())
862 {
863 // Thrown by the abort handler; the report is complete.
864 throw;
865 }
867 }
868#else
869 // Invoke the callable, passing the self reference; the arguments of
870 // test() are bound to it, by bind_callable().
871 callable_ (*this);
872#endif // defined(__cpp_exceptions)
873
874 // this->stopwatch_.stop ();
875 reporter.end_test_case (*this);
876 }
877
878 // ==========================================================================
879
891 template <typename Callable_T, typename... Args_T>
893 Callable_T&& callable, Args_T&&... arguments)
894 : runnable_base{ "", test_runner, 0 },
895 callable_{ detail::bind_callable<suite> (
896 std::forward<Callable_T> (callable),
897 std::forward<Args_T> (arguments)...) },
898 located_name_{ std::move (name) }
899 {
901
902#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
903 trace::printf ("%s '%s' %" MICRO_OS_PLUS_MICRO_TEST_PLUS_PRI_SIZE "\n",
904 __PRETTY_FUNCTION__, name_, own_index_);
905#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
906 }
907
908#if defined(__GNUC__)
909#pragma GCC diagnostic push
910
911#if defined(__clang__)
912#pragma clang diagnostic ignored "-Wdocumentation"
913#endif // defined(__clang__)
914#endif // defined(__GNUC__)
915
964
965 template <typename Callable_T, typename... Args_T>
966 requires detail::test_body<Callable_T, Args_T...>
967 void
968 suite::test (const located_name& name, Callable_T&& callable,
969 Args_T&&... arguments)
970 {
971#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
972 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name.name ());
973#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
974
975 // Called from inside a running test case of this suite; run nested in
976 // it. A running test case of another suite does not qualify; the call
977 // is then rejected below, as for any suite that is not running.
978 if (class test_case* running = running_test_case ();
979 running != nullptr && has_running_test_case_of (*this))
980 {
981 running->test (name, std::forward<Callable_T> (callable),
982 std::forward<Args_T> (arguments)...);
983 return;
984 }
985
986 if (!accepts_test_case (*this, name))
987 {
988 return;
989 }
990
991 create_test_case (name, *this, 1, std::forward<Callable_T> (callable),
992 std::forward<Args_T> (arguments)...);
993 }
994
995#if !defined(__DOXYGEN__)
1001 template <typename Callable_T, typename... Args_T>
1002 requires (!detail::test_body<Callable_T, Args_T...>)
1003 void
1004 suite::test ([[maybe_unused]] const located_name& name,
1005 [[maybe_unused]] Callable_T&& callable,
1006 [[maybe_unused]] Args_T&&... arguments)
1007 {
1008 detail::reject_test_body<Callable_T, Args_T...> ();
1009 }
1010#endif // !defined(__DOXYGEN__)
1011
1012 // --------------------------------------------------------------------------
1013
1014 namespace detail
1015 {
1027 template <class Self_T, class Values_T, class Callable_T>
1028 void
1029 test_each_value (Self_T& self, const located_name& name, Values_T&& values,
1030 Callable_T& callable)
1031 {
1032 std::size_t position = 0;
1033
1034 const auto run_one
1035 = [&self, &name, &callable, &position] (const auto& value)
1036 {
1037 ++position;
1038
1039 std::string text{ name.name () };
1040 text += " (";
1041 if constexpr (extension::printable<
1042 std::remove_cvref_t<decltype (value)>>)
1043 {
1045 }
1046 else
1047 {
1048 text += '#';
1049 plain_text::append (text, position);
1050 }
1051 text += ')';
1052
1053 // The test case runs within this call, so the value and the
1054 // callable are captured by reference, not copied.
1055 self.test (located_name{ std::move (text), name.location () },
1056 [&callable, &value] (auto& test_self)
1057 {
1058 if constexpr (std::is_member_pointer_v<
1059 std::remove_cvref_t<Callable_T>>)
1060 {
1061 std::invoke (callable, test_self, value);
1062 }
1063 else
1064 {
1065 callable (test_self, value);
1066 }
1067 });
1068 };
1069
1070 if constexpr (test_values_range<Values_T>)
1071 {
1072 for (const auto& value : values)
1073 {
1074 run_one (value);
1075 }
1076 }
1077 else
1078 {
1079 std::apply ([&run_one] (const auto&... elements)
1080 { (run_one (elements), ...); }, values);
1081 }
1082 }
1083 } // namespace detail
1084
1090 template <typename Values_T, typename Callable_T>
1093 void
1094 test_case::test_each (const located_name& name, Values_T&& values,
1095 Callable_T&& callable)
1096 {
1097 detail::test_each_value (*this, name, std::forward<Values_T> (values),
1098 callable);
1099 }
1100
1105 template <typename T, typename Callable_T>
1106 void
1108 std::initializer_list<T> values, Callable_T&& callable)
1109 {
1110 detail::test_each_value (*this, name, values, callable);
1111 }
1112
1118 template <typename Values_T, typename Callable_T>
1121 void
1122 suite::test_each (const located_name& name, Values_T&& values,
1123 Callable_T&& callable)
1124 {
1125 detail::test_each_value (*this, name, std::forward<Values_T> (values),
1126 callable);
1127 }
1128
1133 template <typename T, typename Callable_T>
1134 void
1135 suite::test_each (const located_name& name, std::initializer_list<T> values,
1136 Callable_T&& callable)
1137 {
1138 detail::test_each_value (*this, name, values, callable);
1139 }
1140
1141#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
1146 // Deprecated since 6.0.0; the timings are internal.
1147 inline const detail::stopwatch&
1148 suite::timings (void) const noexcept
1149 {
1150 return stopwatch_;
1151 }
1152
1157 // Deprecated since 6.0.0; the timings are internal.
1158 inline detail::stopwatch&
1159 suite::timings (void) noexcept
1160 {
1161 return stopwatch_;
1162 }
1163#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
1164
1165 // ==========================================================================
1166
1177 template <typename Callable_T, typename... Args_T>
1178 requires (!detail::static_suite_body<Callable_T, Args_T...>
1179 && detail::suite_body<Callable_T, Args_T...>)
1181 Callable_T&& callable, Args_T&&... arguments)
1182 : suite{ std::move (name), test_runner,
1183 std::forward<Callable_T> (callable),
1184 std::forward<Args_T> (arguments)... }
1185 {
1186#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
1187 trace::printf ("%s '%s' %" MICRO_OS_PLUS_MICRO_TEST_PLUS_PRI_SIZE "\n",
1188 __PRETTY_FUNCTION__, name_, own_index_);
1189#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
1190
1191 link_or_reject ();
1192 }
1193
1194#if !defined(__DOXYGEN__)
1202 template <typename Callable_T, typename... Args_T>
1203 requires (!detail::static_suite_body<Callable_T, Args_T...>
1204 && !detail::suite_body<Callable_T, Args_T...>)
1205 static_suite::static_suite (located_name name, runner& test_runner,
1206 [[maybe_unused]] Callable_T&& callable,
1207 [[maybe_unused]] Args_T&&... arguments)
1208 : suite{ std::move (name), test_runner, [] (suite&) noexcept {} }
1209 {
1210 detail::reject_suite_body<Callable_T, Args_T...> ();
1211 }
1212#endif // !defined(__DOXYGEN__)
1213
1214#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
1225 // Deprecated since 6.0.0; use a suite& first parameter.
1226 template <typename Callable_T, typename... Args_T>
1227 requires (detail::static_suite_body<Callable_T, Args_T...>)
1229 Callable_T&& callable, Args_T&&... arguments)
1230 : suite{ std::move (name), test_runner,
1231 [stored_callable = std::forward<Callable_T> (callable)] (
1232 suite& self, auto&... stored_arguments) mutable
1233 {
1234 stored_callable (static_cast<static_suite&> (self), stored_arguments...);
1235 },
1236 std::forward<Args_T> (arguments)... }
1237 {
1238#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
1239 trace::printf ("%s '%s' %" MICRO_OS_PLUS_MICRO_TEST_PLUS_PRI_SIZE "\n",
1240 __PRETTY_FUNCTION__, name_, own_index_);
1241#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
1242
1243 link_or_reject ();
1244 }
1245#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
1246
1247 // --------------------------------------------------------------------------
1248} // namespace micro_os_plus::micro_test_plus
1249
1250// ----------------------------------------------------------------------------
1251
1252#if defined(__GNUC__)
1253#pragma GCC diagnostic pop
1254#pragma GCC diagnostic pop
1255#endif // defined(__GNUC__)
1256
1257// ----------------------------------------------------------------------------
1258
1259#endif // defined(__cplusplus)
1260
1261// ----------------------------------------------------------------------------
1262
1263#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_TEST_INLINES_H_
1264
1265// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.8 by Doxygen 1.18.0.