micro-test-plus 5.0.1
µTest++ Testing Framework
Loading...
Searching...
No Matches
test-inlines.h
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
45
46#ifndef MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_TEST_INLINES_H_
47#define MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_TEST_INLINES_H_
48
49// ----------------------------------------------------------------------------
50
51#if defined(__cplusplus)
52
53// ----------------------------------------------------------------------------
54
57
58#if __has_include("micro-os-plus/diag/trace.h")
59#include "micro-os-plus/diag/trace.h"
60#endif // __has_include("micro-os-plus/diag/trace.h")
61
62#include <cstdio>
63#include <cstring>
64
65// ----------------------------------------------------------------------------
66
67#if defined(__GNUC__)
68#pragma GCC diagnostic push
69
70#pragma GCC diagnostic ignored "-Waggregate-return"
71#if defined(__clang__)
72#pragma clang diagnostic ignored "-Wunknown-warning-option"
73#pragma clang diagnostic ignored "-Wc++98-compat"
74#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
75#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
76#else // GCC only
77#pragma GCC diagnostic ignored "-Wredundant-tags"
78#endif // defined(__clang__)
79#endif // defined(__GNUC__)
80
81// ============================================================================
82
84{
85 namespace detail
86 {
87 // ========================================================================
88
93 inline const char*
94 test_node::name (void) const noexcept
95 {
96 return name_;
97 }
98
103 inline runner_totals&
105 {
106 return totals_;
107 }
108
113 inline const runner_totals&
114 test_node::totals () const noexcept
115 {
116 return totals_;
117 }
118
119 // ========================================================================
120
125 inline size_t
126 runnable_base::own_index () const noexcept
127 {
128 return own_index_;
129 }
130
135 inline void
136 runnable_base::own_index (size_t index) noexcept
137 {
138 own_index_ = index;
139 }
140
145 inline size_t
147 {
149 }
150
157 inline size_t
162
167 inline size_t
169 {
170 return children_subtests_.size ();
171 }
172
177 inline class runner&
178 runnable_base::runner (void) const noexcept
179 {
180 return runner_;
181 }
182
183 // ========================================================================
184
192 template <typename Self_T>
193 template <typename Callable_T, typename... Args_T>
195 size_t own_index, Callable_T&& callable,
196 Args_T&&... arguments)
198 {
199 // When there are no extra arguments the callable already has the
200 // signature void(Self_T&), so store it directly. Only use std::bind when
201 // additional arguments must be pre-bound, to avoid triggering a GCC ARM
202 // bug in
203 // __is_nothrow_invocable<_Bind<...>, Self_T&> (GCC 15.2.1).
204 if constexpr (sizeof...(arguments) == 0)
205 {
206 callable_ = std::forward<Callable_T> (callable);
207 }
208 else
209 {
210 callable_ = std::bind (std::forward<Callable_T> (callable),
211 std::placeholders::_1,
212 std::forward<Args_T> (arguments)...);
213 }
215#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
216 trace::printf ("%s '%s' %zu\n", __PRETTY_FUNCTION__, name, own_index_);
217#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
218 }
219
224 * trace message identifying the instance being destroyed.
225 */
226 template <typename Self_T>
228 {
229#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
230 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name_);
231#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
232 }
233
234 // ------------------------------------------------------------------------
235 } // namespace detail
236
237 // ==========================================================================
238
244 template <typename Callable_T, typename... Args_T>
245 subtest::subtest (const char* name, class runner& runner,
246 suite& parent_suite, size_t own_index,
247 size_t nesting_depth, Callable_T&& callable,
248 Args_T&&... arguments)
250 std::forward<Callable_T> (callable),
251 std::forward<Args_T> (arguments)... },
252 parent_suite_{ parent_suite }, nesting_depth_{ nesting_depth }
253 {
254#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
255 trace::printf ("%s '%s' %zu %zu\n", __PRETTY_FUNCTION__, name, own_index_,
256 nesting_depth_);
257#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
258 }
259
266 template <typename Callable_T, typename... Args_T>
267 void
268 subtest::test (const char* name, Callable_T&& callable,
269 Args_T&&... arguments)
270 {
271#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
272 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name);
273#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
274
276 auto child_subtest = std::make_unique<subtest> (
278 std::forward<Callable_T> (callable),
279 std::forward<Args_T> (arguments)...);
280
281 after_subtest_create_ (std::move (child_subtest), parent_suite_);
282 }
283
290 template <class Expr_T>
292 auto
293 subtest::expect (const Expr_T& expr, const reflection::source_location& sl)
294 {
295 return detail::deferred_reporter{ expr, false, sl, *this,
296 reporter ().expression () };
297 }
298
305 template <class Expr_T>
307 auto
308 subtest::assume (const Expr_T& expr, const reflection::source_location& sl)
309 {
310 return detail::deferred_reporter{ expr, true, sl, *this,
311 reporter ().expression () };
312 }
313
314 /**
315 * @details
316 * Top-level subtests (direct children of a `suite`) have depth 1.
317 * Each additional level of nesting increments the depth by 1.
318 */
319 inline size_t
320 subtest::nesting_depth () const noexcept
321 {
322 return nesting_depth_;
323 }
324
325 // ==========================================================================
326
328 * @details
329 * Delegates to `runnable`, which binds the callable with
330 * its arguments. After construction, the suite is registered with the
331 * static test runner.
332 */
333 template <typename Callable_T, typename... Args_T>
334 suite::suite (const char* name, class runner& runner, Callable_T&& callable,
335 Args_T&&... arguments)
336 : runnable<suite>{ name, runner, 0, std::forward<Callable_T> (callable),
337 std::forward<Args_T> (arguments)... }
339#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
340 trace::printf ("%s '%s' %zu\n", __PRETTY_FUNCTION__, name, own_index_);
341#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
342 }
343
344#if defined(__GNUC__)
345#pragma GCC diagnostic push
346
347#if defined(__clang__)
348#pragma clang diagnostic ignored "-Wdocumentation"
349#endif // defined(__clang__)
350#endif // defined(__GNUC__)
351
358 * the provided name, and its execution is managed by the framework's test
359 * runner.
360 *
361 * Each test case typically involves evaluating a logical expression, such as
362 * comparing a computed result to an expected value. For C++ projects, it is
363 * also possible to verify whether evaluating an expression throws
364 * exceptions. Each test either succeeds or fails, and for expectations, the
365 * test runner maintains counts of successful and failed checks.
366 *
367 * This function template enables flexible and expressive test case
368 * definitions, supporting both parameterised and non-parameterised tests. It
369 * is typically invoked at global scope or within test suite definitions to
370 * ensure automatic registration and execution.
371 *
372 * A test case is characterised by a name, a function that performs the
373 * checks, and optionally, arguments to be passed to that function. The
374 * implementation of `test` invokes the provided function with the given
375 * arguments and reports the results to the test runner.
376 *
377 * @par Example
378 *
379 * @code{.cpp}
380 * namespace mt = micro_os_plus::micro_test_plus;
381 *
382 * ts.test ("Check answer with comparator", [] (auto& t) {
383 * t.expect (mt::eq (compute_answer (), 42)) << "answer is 42";
384 * });
385 * @endcode
386 */
387
388 template <typename Callable_T, typename... Args_T>
389 void
390 suite::test (const char* name, Callable_T&& callable, Args_T&&... arguments)
391 {
392#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED)
393 trace::printf ("%s '%s'\n", __PRETTY_FUNCTION__, name);
394#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_ENABLED
395
397 auto child_subtest
398 = std::make_unique<subtest> (name, runner (), *this, own_index, 1,
399 std::forward<Callable_T> (callable),
400 std::forward<Args_T> (arguments)...);
401
402 after_subtest_create_ (std::move (child_subtest), *this);
403 }
404
409 inline detail::timestamps&
410 suite::timings () noexcept
411 {
412 return timings_;
413 }
414
419 inline const detail::timestamps&
420 suite::timings () const noexcept
421 {
422 return timings_;
423 }
424
425 // ==========================================================================
426
434 inline void
435 top_suite::name (const char* new_name) noexcept
436 {
437 name_ = new_name;
438 }
439
440 // ==========================================================================
441
448 template <typename Callable_T, typename... Args_T>
450 Callable_T&& callable, Args_T&&... arguments)
451 // The nullptr passed to the base constructor is an optimisation to save
452 // some space, since this callble is not used by the static runner.
453 : suite{ name, detail::to_runner (runner), nullptr }
454 {
455 if constexpr (sizeof...(arguments) == 0)
456 {
457 static_callable_ = std::forward<Callable_T> (callable);
458 }
459 else
460 {
461 static_callable_ = std::bind (std::forward<Callable_T> (callable),
462 std::placeholders::_1,
463 std::forward<Args_T> (arguments)...);
464 }
465
466#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED)
467 trace::printf ("%s '%s' %zu\n", __PRETTY_FUNCTION__, name, own_index_);
468#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_TRACE_CONSTRUCTORS_ENABLED
471 }
472
473 // --------------------------------------------------------------------------
474} // namespace micro_os_plus::micro_test_plus
475
476// ----------------------------------------------------------------------------
477
478#if defined(__GNUC__)
479#pragma GCC diagnostic pop
480#pragma GCC diagnostic pop
481#endif // defined(__GNUC__)
482
483// ----------------------------------------------------------------------------
484
485#endif // defined(__cplusplus)
486
487// ----------------------------------------------------------------------------
488
489#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_TEST_INLINES_H_
490
491// ----------------------------------------------------------------------------
Deferred reporter class for a specific expression.
class runner & runner(void) const noexcept
Gets the test runner associated with this test runnable.
size_t current_subtest_index() const noexcept
Returns the index of the most recently created child subtest.
runnable_base(const char *name, runner &runner, size_t own_index)
Constructs a runnable_base with a name, runner, and index.
Definition test.cpp:118
void after_subtest_create_(std::unique_ptr< subtest > child_test, suite &suite)
Definition test.cpp:177
size_t own_index_
The test index, counting from 1.
Definition test.h:415
size_t own_index() const noexcept
Returns the positional index of this object within its parent.
size_t current_subtest_index_
The subtest index, counting from 1.
Definition test.h:426
class reporter & reporter(void) const noexcept
Definition test.cpp:149
size_t children_subtests_count(void) const noexcept
Returns the number of direct child subtests owned by this node.
class runner & runner_
Reference to the test runner that owns this object.
Definition test.h:410
size_t increment_subtest_index() noexcept
Increments and returns the child subtest sequential index.
std::vector< std::unique_ptr< subtest > > children_subtests_
Owning collection of direct child subtests.
Definition test.h:436
runnable(const char *name, class runner &runner, size_t own_index, Callable_T &&callable, Args_T &&... arguments)
Class template constructor.
virtual ~runnable() override
Virtual destructor.
Aggregated pass/fail/subtest counters for a node in the test tree.
const char * name(void) const noexcept
Gets the node name.
runner_totals totals_
Totals for the test node, including nested cases.
Definition test.h:238
runner_totals & totals() noexcept
Gets the totals for the test.
const char * name_
The test node name.
Definition test.h:233
A begin/end timestamp pair used to measure elapsed time.
Definition timings.h:214
Local implementation of source location information for diagnostics.
Definition reflection.h:150
detail::expression_formatter & expression()
Provides access to the expression formatter for this reporter.
The test runner for the µTest++ framework.
Definition runner.h:144
A runner variant that also manages statically-registered test suites.
Definition runner.h:378
std::function< void(static_suite &)> static_callable_
Callable storing the static suite body and any bound arguments. Invoked with a reference to the concr...
Definition test.h:999
static_suite(const char *name, static_runner &runner, Callable_T &&callable, Args_T &&... arguments)
Class template constructor for static_suite.
suite & parent_suite_
Reference to the parent suite that owns this subtest.
Definition test.h:692
void test(const char *name, Callable_T &&callable, Args_T &&... arguments)
Adds a test case to the suite.
subtest(const char *name, class runner &runner, suite &parent_suite, size_t own_index, size_t nesting_depth, Callable_T &&callable, Args_T &&... arguments)
Constructs a subtest with a name, runner, parent suite, index, nesting depth, and callable.
size_t nesting_depth_
The nesting depth of this subtest within the suite.
Definition test.h:697
size_t nesting_depth() const noexcept
Returns the nesting depth of this subtest.
A named, runnable test suite registered with the test runner.
Definition test.h:723
suite(const char *name, class runner &runner, Callable_T &&callable, Args_T &&... arguments)
Constructs a suite with a name, runner, and callable body.
detail::timestamps timings_
Timing measurements for this suite's execution.
Definition test.h:827
detail::timestamps & timings() noexcept
Gets the timings for this suite.
void test(const char *name, Callable_T &&callable, Args_T &&... arguments)
Adds a test case to the suite.
const char * name(void) const noexcept
Gets the node name.
C++20 concept satisfied when a type can be used as a test expression in expect() or assume().
C++ header file with declarations for the µTest++ deferred reporter.
auto assume(const Expr_T &expr, const reflection::source_location &sl=reflection::source_location::current())
Check a condition and, if false, abort test execution.
auto expect(const Expr_T &expr, const reflection::source_location &sl=reflection::source_location::current())
Evaluate a generic condition and report the results.
Internal implementation details for the µTest++ framework.
void register_static_suite(static_runner &static_runner_ref, static_suite &static_suite_ref)
Registers a static suite with a static runner.
Definition runner.cpp:95
Primary namespace for the µTest++ testing framework.
C++ header file with declarations for the µTest++ test reporter.