micro-test-plus 6.0.0
µTest++ Testing Framework
Loading...
Searching...
No Matches
micro_os_plus::micro_test_plus::detail::check_result Class Reference

The result of one check, reported when destroyed. More...

#include "micro-os-plus/micro-test-plus.h"

Public Member Functions

 check_result (check_result &&)=delete
 Deleted move constructor to prevent moving.
 check_result (const check_result &)=delete
 Deleted copy constructor to prevent copying.
 check_result (void)
 Constructs a result in the initial state.
 ~check_result () noexcept(false)
 Destructor that reports the check.
template<class Expr_T>
void init (const Expr_T &expr, check_kind kind, const reflection::source_location &location, test_case &test_case, expression_formatter &expression, bool writes_passed)
 Initialises the result with the check of an expression.
template<class T>
requires detail::message_argument<T>
check_result && operator<< (const T &msg) &&
 Appends a message to the check.
check_result & operator= (check_result &&)=delete
 Deleted move assignment operator to prevent moving.
check_result & operator= (const check_result &)=delete
 Deleted copy assignment operator to prevent copying.
bool value (void) const &&noexcept
 Retrieves the result value.

Private Member Functions

void start (bool value, check_kind kind, bool has_expression, const reflection::source_location &location, test_case &test_case, bool writes_passed)
 Starts the result of a check from its parts.

Private Attributes

failure_info info_
 The details displayed when the check fails.
bool is_written_ = false
 Whether the outcome of the check is written.
test_case * test_case_ = nullptr
 Pointer to the test case invoking this check.
int uncaught_exceptions_ = 0
 The number of uncaught exceptions when the check was created.
bool value_ = false
 Stores the result value of the check.

Detailed Description

Created by the caller of expect() and require() as a temporary, the defaulted last argument of these functions, it lives until the end of the expression statement, and the functions return a reference to it, so that a result cannot be kept in a variable (auto r = t.expect (...) does not compile). The check is reported when the result is destroyed, at the end of the statement, in the order of the statements, and not later.

Warning
The result must never be named: a reference bound to it (auto&& r = t.expect (...)) compiles, but dangles after the statement. The message operator and value() can be called only on a temporary, so the named result cannot be used, and forcing it with std::move() is undefined. The messages are appended in the same statement.

A result in the initial state, not yet initialised, reports nothing.

Its template function init() evaluates the expression and formats it, with the actual operand values, into the expression formatter of the reporter, which keeps the text for this check until it is reported, so that it can be displayed on failure. The messages passed via operator<<() are collected into a string. The destructor reports the outcome, updates the counters of the test case, and, after a failed require(), stops the test case.

A passed check whose line the reporter never writes (see reporter::writes_passed_checks()) formats neither its expression nor its messages; it is counted, and passed to reporter::pass_without_text(), which records only its test point.

Only the template function init() depends on the type of the expression; it delegates to a private non-template function, and the destructor, which does the reporting, is not a template either, so this code is compiled once, in src/check-result.cpp, not for each type of expression.

Definition at line 208 of file check-result.h.

Constructor & Destructor Documentation

◆ check_result() [1/3]

micro_os_plus::micro_test_plus::detail::check_result::check_result ( void )
default

Used for the temporary that the caller of expect() and require() creates as their defaulted argument; the result reports nothing unless it is initialised by init(). Defined in src/check-result.cpp, so that the call sites only call it.

The members have their initial values: the result is not initialised, has no test case, and its destructor reports nothing.

References init().

Referenced by check_result(), check_result(), operator<<(), operator=(), and operator=().

◆ check_result() [2/3]

micro_os_plus::micro_test_plus::detail::check_result::check_result ( const check_result & )
delete

Each instance reports exactly one check, from its destructor; a copy would report it twice.

References check_result().

◆ check_result() [3/3]

micro_os_plus::micro_test_plus::detail::check_result::check_result ( check_result && )
delete

The instance is a temporary that lives only until the end of the expression statement; the functions that fill it return a reference to it, so that a result cannot be kept in a variable.

References check_result().

◆ ~check_result()

micro_os_plus::micro_test_plus::detail::check_result::~check_result ( )

It may throw failed_requirement, after a failed require(), to stop the test case, hence noexcept (false).

The destructor reports the result of the check, at the end of the expression statement, after all the user messages were appended.

The test point number of the test case is advanced first, so that the check numbers follow the order in which the checks are reported (the first check is reported as check #1), also when a check is reported after checks that began later, such as those made by a function called in its arguments.

If the evaluated expression is true, the reporter records a successful outcome along with any accumulated message, or, if it does not write the passed checks, only the test point, with reporter::pass_without_text(), and the passed checks counter of the test case is incremented. If the expression is false, the reporter records a failure, with the failure details (the kind of the check, the message, and the source location) and the expression, and the failed checks counter is incremented.

The expression text is obtained from the expression formatter with check_text(), which returns the text of this check even if later checks used the formatter, and is released with end_check().

If the check is a require() and the test expression has failed, the test case is stopped: when exceptions are enabled, by throwing failed_requirement, caught by test_case::run(), as the fatal assertions of most C++ test frameworks do; the run then continues with the next test case. If the destructor runs during stack unwinding (caused by an exception thrown while the rest of the statement is evaluated, such as by the argument of a message), it does not throw, since the test case is already being stopped.

Without exceptions, the test execution is aborted via the runner, whose reporter writes the buffered test output, in place of the end_test_case() that is never reached (see runner::abort()).

Definition at line 139 of file check-result.cpp.

140 {
141#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
142 trace::printf ("%s\n", __PRETTY_FUNCTION__);
143#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
144
145 // A result that was not initialised, such as a default argument
146 // whose function did not run, reports nothing.
147 if (test_case_ == nullptr) [[unlikely]]
148 {
149 return;
150 }
151
152 // The index starts at 0; it must be incremented before the check is
153 // reported, so that the first check is reported as check #1.
155
156 // The text formatted by this check, even if the formatter was used by
157 // other checks in the meantime (when this result was kept).
158 auto& expression = test_case_->test_reporter ().expression_;
159 const std::string& expression_str = expression.check_text (this);
160
161 if (value_) [[likely]]
162 {
163 if (is_written_)
164 {
165 test_case_->test_reporter ().pass (info_.message, expression_str,
166 *test_case_);
167 }
168 else
169 {
171 }
173 }
174 else
175 {
176 test_case_->test_reporter ().fail (info_, expression_str,
177 *test_case_);
179 }
180
181 expression.end_check (this);
182
183 if (info_.kind == check_kind::requirement && !value_) [[unlikely]]
184 {
185#if defined(__cpp_exceptions)
186 if (std::uncaught_exceptions () <= uncaught_exceptions_)
187 {
188 throw failed_requirement{};
189 }
190#else
192#endif // defined(__cpp_exceptions)
193 }
194 }
void increment_passed_checks(size_t count=1) noexcept
Increments the passed-checks counter.
void increment_failed_checks(size_t count=1) noexcept
Increments the failed-checks counter.
int uncaught_exceptions_
The number of uncaught exceptions when the check was created.
test_case * test_case_
Pointer to the test case invoking this check.
bool is_written_
Whether the outcome of the check is written.
bool value_
Stores the result value of the check.
failure_info info_
The details displayed when the check fails.
const std::string & check_text(const void *owner) const noexcept
Returns the expression text of a check.
void fail(const failure_info &failure, const std::string &expression, const test_case &test_case)
Reports a failed condition.
Definition reporter.cpp:771
virtual void pass_without_text(const test_case &test_case)=0
Reports a passed condition whose text is not written.
expression_formatter expression_
Expression formatter for pass and fail reporting.
Definition reporter.h:1423
void pass(const std::string &message, const std::string &expression, const test_case &test_case)
Reports a passed condition.
Definition reporter.cpp:751
size_t next_test_point_number(void) noexcept
Advances to the next child test point, and returns its number.
void abort(const reflection::source_location &sl=reflection::source_location::current()) const
Aborts test execution via the owning runner, for a failed requirement.
Definition test.cpp:306
class reporter & test_reporter(void) const noexcept
Returns the test reporter associated with this test runnable.
Definition test.cpp:155
reflection::source_location location
The source location of the check.
std::string message
The message passed via operator<<(), if any.

References micro_os_plus::micro_test_plus::detail::runnable_base::abort(), micro_os_plus::micro_test_plus::detail::expression_formatter::check_text(), micro_os_plus::micro_test_plus::detail::reporter::expression_, micro_os_plus::micro_test_plus::detail::reporter::fail(), micro_os_plus::micro_test_plus::counters::increment_failed_checks(), micro_os_plus::micro_test_plus::counters::increment_passed_checks(), info_, is_written_, micro_os_plus::micro_test_plus::detail::failure_info::kind, micro_os_plus::micro_test_plus::detail::failure_info::location, micro_os_plus::micro_test_plus::detail::failure_info::message, micro_os_plus::micro_test_plus::detail::runnable_base::next_test_point_number(), micro_os_plus::micro_test_plus::detail::reporter::pass(), micro_os_plus::micro_test_plus::detail::reporter::pass_without_text(), micro_os_plus::micro_test_plus::detail::requirement, test_case_, micro_os_plus::micro_test_plus::detail::runnable_base::test_reporter(), micro_os_plus::micro_test_plus::detail::runnable_base::totals_, uncaught_exceptions_, and value_.

Member Function Documentation

◆ init()

template<class Expr_T>
void micro_os_plus::micro_test_plus::detail::check_result::init ( const Expr_T & expr,
check_kind kind,
const reflection::source_location & location,
test_case & test_case,
expression_formatter & expression,
bool writes_passed )

The expression is evaluated, and, if the outcome is written, formatted into the expression formatter.

Template Parameters
Expr_TThe type of the expression.
Parameters
exprThe expression under evaluation.
kindThe kind of the check, check_kind::requirement for require(), whose failure stops the test case.
locationThe source location of the check.
test_caseThe test case that owns this check.
expressionThe expression formatter to format the expression text.
writes_passedWhether the reporter writes the passed checks; if not, a passed check is not formatted.
Returns
Nothing.

The expression is evaluated, and the result is passed, together with the kind of the check and whether the expression is a framework comparator, to the non-template start(), which also stores the source location and the test case. The kind, check_kind::requirement for require(), determines whether the test case is stopped if the check fails.

The expression formatter of the reporter, which is shared by all checks, is then assigned to this check by begin_check(), which sets aside the text of a previous check not yet reported, if any. The expression is formatted into it only when the outcome is written: on failure, or, if writes_passed, also on success. Otherwise the text of this check stays empty, and the operands, which may be application types with an expensive operator<<, are not formatted.

An expression that is only explicitly convertible to bool, such as a std::optional or a std::unique_ptr, is written as the result of the conversion, true or false, since that is what the check tested; its own value (the contents of a std::optional<bool>, for example) could contradict the outcome, and many such types cannot be formatted at all.

The decision is taken here, while the operands are alive.

Definition at line 102 of file check-result-inlines.h.

106 {
107 start (static_cast<bool> (expr), kind, extension::is_op<Expr_T>,
108 location, test_case, writes_passed);
109
110#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
111 trace::printf ("%s\n", __PRETTY_FUNCTION__);
112#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
113
114 // The same address is used by the destructor, to retrieve the text,
115 // even if other checks used the formatter in the meantime.
116 expression.begin_check (this);
117
118 if (is_written_)
119 {
120 if constexpr (extension::is_op<Expr_T>
121 or std::convertible_to<Expr_T, bool>)
122 {
123 expression << expr;
124 }
125 else
126 {
127 expression << static_cast<bool> (expr);
128 }
129 }
130 }
void start(bool value, check_kind kind, bool has_expression, const reflection::source_location &location, test_case &test_case, bool writes_passed)
Starts the result of a check from its parts.

References micro_os_plus::micro_test_plus::detail::expression_formatter::begin_check(), is_written_, and start().

Referenced by check_result(), micro_os_plus::micro_test_plus::test_case::expect(), micro_os_plus::micro_test_plus::test_case::report_rejected_test(), and micro_os_plus::micro_test_plus::test_case::require().

◆ operator<<()

template<class T>
requires detail::message_argument<T>
check_result && micro_os_plus::micro_test_plus::detail::check_result::operator<< ( const T & msg) &&

Callable only on a temporary, like the result of expect() and require(), which is destroyed at the end of the statement: a result bound to a name (auto&& r = t.expect (...)) is not usable, since it would refer to a destroyed object after the statement.

Template Parameters
TThe type of the message to append: a value that the formatter can write, or a callable object that returns one (see detail::message_argument); any other argument is rejected at compile time, with a message that states what is accepted.
Parameters
msgThe message to append, or the callable object that returns it, called only if the check fails.
Returns
The current check result, as an rvalue reference, so that further messages can be appended in the same expression.

This operator overload enables the check result to accumulate expectation messages by appending the provided value to the internal message string.

The value is written by the same operator<< overloads as the operands of the expressions, so that both accept the same types, including the application types with an operator<< for the formatter. The message is moved into a local formatter in the plain mode, which writes the numbers without the type suffixes, and the strings and the char values without quotes, with only the control characters that can change the state of a terminal escaped, and is moved back after the value is appended, so that no other buffer is allocated. The formatter of the reporter is not used, since it may hold the expression of this check, or of a pending one.

When the outcome of the check is not written, the message is not needed, and the value is not formatted.

A callable object (see detail::lazy_message) is called only if the check failed, and its result is appended as a value; after a success, even if the outcome is written, it appends nothing, since its purpose is to describe a failure with values that may not be valid otherwise, like the error of a std::expected that holds a value. Being a class type, a lambda without captures is called too, although it is also printable, as a bool.

Definition at line 177 of file check-result-inlines.h.

179 {
180 if constexpr (detail::lazy_message<T>)
181 {
182 if (value_)
183 {
184 return std::move (*this);
185 }
186 }
187 else
188 {
189 if (!is_written_)
190 {
191 return std::move (*this);
192 }
193 }
194
195 expression_formatter formatter{ std::move (info_.message) };
196 if constexpr (detail::lazy_message<T>)
197 {
198 formatter << std::invoke (msg);
199 }
200 else
201 {
202 formatter << msg;
203 }
204 info_.message = std::move (formatter.buffer_);
205 return std::move (*this);
206 }
detail::expression_formatter expression_formatter
Public name of the formatter used to report operand values.

References check_result(), micro_os_plus::micro_test_plus::detail::expression_formatter::buffer_, info_, is_written_, and value_.

◆ operator=() [1/2]

check_result & micro_os_plus::micro_test_plus::detail::check_result::operator= ( check_result && )
delete

Each instance reports exactly one check, from its destructor.

References check_result(), start(), and value().

◆ operator=() [2/2]

check_result & micro_os_plus::micro_test_plus::detail::check_result::operator= ( const check_result & )
delete

Each instance reports exactly one check, from its destructor.

References check_result().

◆ start()

void micro_os_plus::micro_test_plus::detail::check_result::start ( bool value,
check_kind kind,
bool has_expression,
const reflection::source_location & location,
test_case & test_case,
bool writes_passed )
private

The non-template part of the initialisation, shared by all the instantiations of the template function init(), which calls it. Marks the result as initialised, so that the destructor reports it.

Parameters
valueThe result of the evaluated expression.
kindThe kind of the check.
has_expressionIndicates whether the expression is a framework comparator, whose text is displayed on failure.
locationThe source location of the check.
test_caseThe test case that owns this check.
writes_passedWhether the reporter writes the passed checks.
Returns
Nothing.

Stores the evaluated Boolean value, and, in the failure details, the kind of the check, the has_expression flag, and the location identifying the source line of the check, with an empty message; it also stores a pointer to the owning test_case, which marks the result as initialised. The outcome is written when the check failed, or when the reporter writes the passed checks (writes_passed). The check number is assigned later, by the destructor, when the check is reported.

When exceptions are enabled, the number of uncaught exceptions is also stored, so that the destructor can tell whether it runs during stack unwinding.

Definition at line 89 of file check-result.cpp.

92 {
93 value_ = value;
94 is_written_ = !value || writes_passed;
95 info_ = failure_info{ kind, {}, has_expression, location };
96 test_case_ = &test_case;
97#if defined(__cpp_exceptions)
98 uncaught_exceptions_ = std::uncaught_exceptions ();
99#endif // defined(__cpp_exceptions)
100 }
bool value(void) const &&noexcept
Retrieves the result value.

References info_, is_written_, test_case_, uncaught_exceptions_, value(), and value_.

Referenced by init(), and operator=().

◆ value()

bool micro_os_plus::micro_test_plus::detail::check_result::value ( void ) const &&
inlinenodiscardnoexcept

Callable only on a temporary, for the reason given for operator<<().

Parameters
None.
Return values
trueThe checked condition was met.
falseThe checked condition was not met.

Returns the result value stored in value_.

Definition at line 139 of file check-result-inlines.h.

140 {
141 return value_;
142 }

References value_.

Referenced by operator=(), and start().

Member Data Documentation

◆ info_

failure_info micro_os_plus::micro_test_plus::detail::check_result::info_
private

The kind, the expression flag, and the location are set by start(); the message is collected via operator<<(). Passed to the reporter by the destructor; the message is also displayed when the check passes.

Definition at line 412 of file check-result.h.

Referenced by ~check_result(), operator<<(), and start().

◆ is_written_

bool micro_os_plus::micro_test_plus::detail::check_result::is_written_ = false
private

Set by start() for a failed check, and for a passed one when the reporter writes the passed checks. When not set, the expression and the messages are not formatted, and the destructor only updates the counters.

Definition at line 401 of file check-result.h.

Referenced by ~check_result(), init(), operator<<(), and start().

◆ test_case_

test_case* micro_os_plus::micro_test_plus::detail::check_result::test_case_ = nullptr
private

Provides access to the reporter and to the totals to be updated. Null in the initial state, which marks a result that was not initialised and reports nothing.

Definition at line 436 of file check-result.h.

Referenced by ~check_result(), and start().

◆ uncaught_exceptions_

int micro_os_plus::micro_test_plus::detail::check_result::uncaught_exceptions_ = 0
private

If the destructor finds more, it runs during stack unwinding (an exception was thrown while the rest of the statement was evaluated, for example by the argument of a message), and must not throw. Present only when exceptions are enabled.

Definition at line 425 of file check-result.h.

Referenced by ~check_result(), and start().

◆ value_

bool micro_os_plus::micro_test_plus::detail::check_result::value_ = false
private

The boolean value of the checked expression, computed by the template function init().

Definition at line 390 of file check-result.h.

Referenced by ~check_result(), operator<<(), start(), and value().


The documentation for this class was generated from the following files: