Files
qt6-migration-pipeline/external/rti_connext_dds-7.7.0/include/ndds/rtitest/TestAssertion.hpp
T
torstenandCopilot 342ffd3e5c Update submodule to latest BUILD_RTI fixes
- HandlerMinelayerData: BUILD_RTI Guards für RTI-Abhängigkeiten
- svcTabCommon: Export-Header-Fix via Fallback-Target
- TrainingAreaBase: Stub-Header für BUILD_RTI=OFF
- ddsfts: Include-Pfad und Dependency-Fixes
- BUILD_RTI=OFF Compilation vollständig unterstützt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: 01M0JNZA586SACX1PRZ2DJAE5S
2026-08-21 19:30:20 +02:00

525 lines
17 KiB
C++

/*
* (c) Copyright, Real-Time Innovations, 2014-2016.
* All rights reserved.
*
* No duplications, whole or partial, manual or electronic, may be made
* without express written permission. Any such copies, or
* revisions thereof, must display this notice unaltered.
* This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_TEST_ASSERTIONS_HPP_
#define RTI_TEST_ASSERTIONS_HPP_
#include <cmath>
#include <iostream>
#include <limits>
#include <stdio.h>
#include <stdlib.h>
#include <stdexcept>
#include <string>
#include <sstream>
#include "rtitest/test_log.h"
#include "rtitest/test_time.h"
namespace rti { namespace test {
/**
* @defgroup RTITestUtilsCPP C++ Utilities
* @brief A set of definitions to help writing unit tests for Modern C++ code.
*/
// --- TestFailure exception: -------------------------------------------------
/**
* @class TestFailure
* @brief Represents a test failure exception.
*
* This class is derived from std::logic_error and is used to indicate a failure in a test case.
* It provides information about the file, line, and message associated with the failure.
*/
class TestFailure : public std::logic_error {
public:
/** @internal
* @brief Constructs a TestFailure object with the specified file, line, and message.
* @param the_file \b In. The file where the failure occurred.
* @param the_line \b In. The line number where the failure occurred.
* @param the_msg \b In. The message describing the failure.
*/
TestFailure(
const std::string& the_file,
const std::string& the_line,
const std::string& the_msg)
: std::logic_error(the_file + ":" + the_line + ": " + the_msg),
_file(the_file),
_line(the_line),
_msg(the_msg)
{ }
/** @internal
* @brief Constructs a TestFailure object with the specified file, line, message, and nested exception.
* @param the_file \b In. The file where the failure occurred.
* @param the_line \b In. The line number where the failure occurred.
* @param the_msg \b In. The message describing the failure.
* @param nested_ex \b In. The nested exception that caused the failure.
*/
TestFailure(
const std::string& the_file,
const std::string& the_line,
const std::string& the_msg,
const std::exception& nested_ex)
: std::logic_error(
the_file + ":" + the_line + ": " + the_msg
+ " - caused by:\n " + nested_ex.what()),
_file(the_file),
_line(the_line),
_msg(the_msg)
{ }
/** @internal
* @brief Destructor for TestFailure.
*
* This destructor is declared with a no-throw exception specification, because
* the base class std::logic_error has a no-throw destructor. Compilation will fail otherwise.
*/
~TestFailure() throw()
{ }
/** @internal
* @brief Get the file where the failure occurred.
* @return The file path as a const reference to std::string.
*/
const std::string& file() const
{
return _file;
}
/** @internal
* @brief Get the line number where the failure occurred.
* @return The line number as a const reference to std::string.
*/
const std::string& line() const
{
return _line;
}
/** @internal
* @brief Get the message describing the failure.
* @return The failure message as a const reference to std::string.
*/
const std::string& msg() const
{
return _msg;
}
private:
std::string _file; /**< The file where the failure occurred. */
std::string _line; /**< The line number where the failure occurred. */
std::string _msg; /**< The message describing the failure. */
};
/**
* @class TestIncomplete
* @brief Exception class for indicating that a test is unsupported.
*
* This exception is thrown when a test is marked as unsupported. It inherits
* from std::logic_error.
*/
class TestIncomplete : public std::logic_error {
public:
/** @internal
* @brief Constructor for TestIncomplete.
* @param retcode \b In. The return code associated with the incomplete test.
* @param reason \b In. The reason for the test being incomplete.
*/
TestIncomplete(int retcode, const std::string& reason)
: std::logic_error(reason), retcode_(retcode)
{ }
/** @internal
* @brief Destructor for TestIncomplete.
*
* This destructor is defined with a no-throw exception specification, because
* the base class std::logic_error has a no-throw destructor. Compilation will
* fail if this destructor is not defined with a no-throw exception specification.
*/
~TestIncomplete() throw()
{ }
/** @internal
* @brief Get the return code associated with the incomplete test.
* @return The return code.
*/
int retcode() const
{
return retcode_;
}
private:
int retcode_; /**< The return code associated with the incomplete test. */
};
}} // namespace rti::test
/**
* @defgroup RTITestAssertionsCPP Assertions
* @brief A set of definitions that implements assertions for Modern C++ code.
*/
// --- Assertion macros: ------------------------------------------------------
/** @internal
* @ingroup RTITestAssertionsCPP
* @brief Throw an exception
* @param expression__ \b In. The expression that raises the exception.
*/
#define RTI_TEST_THROW(expression__) \
throw rti::test::TestFailure( \
"" __FILE__, \
"" RTI_TEST_TOSTRING(__LINE__), \
"assertion failed: " #expression__)
/**
* @ingroup RTITestAssertionsCPP
* @brief Asserts that the given expression is true. Otherwise, throw a TestFailure exception.
* @param expression__ \b In. The expression to be evaluated.
*/
#define RTI_TEST_ASSERT(expression__) \
if (!(expression__)) { \
RTI_TEST_THROW(expression__); \
}
/**
* @ingroup RTITestAssertionsCPP
* @brief Throw an exception with a given message.
* @param msg__ \b In. The message to be included in the exception.
*/
#define RTI_TEST_FAIL(msg__) \
throw rti::test::TestFailure( \
"" __FILE__, \
"" RTI_TEST_TOSTRING(__LINE__), \
msg__);
/**
* @ingroup RTITestAssertionsCPP
* @brief Throw a TestIncomplete exception with the unsupported return code.
* @param reason__ \b In. The reason for the test being incomplete.
*/
#define RTI_TEST_THROW_UNSUPPORTED(reason__) \
throw rti::test::TestIncomplete(RTI_TEST_RETCODE_UNSUPPORTED, reason__)
/** @internal
* @ingroup RTITestAssertionsCPP
* @brief Throw a TestIncomplete exception with the unimplemented return code.
* @param reason__ \b In. The reason for the test being incomplete.
*/
#define RTI_TEST_THROW_UNIMPLEMENTED(reason__) \
throw rti::test::TestIncomplete(RTI_TEST_RETCODE_UNIMPLEMENTED, reason__)
template <typename T, typename U>
/** @internal
* @ingroup RTITestAssertionsCPP
* @brief Prints a failure message saying the assertion `a == b` failed.
* @tparam T \b In. The type of the first value.
* @tparam U \b In. The type of the second value.
* @param a \b In. The first value to compare.
* @param b \b In. The second value to compare.
*/
void print_assert_equals_failure(const T& a, const U& b)
{
std::ostringstream os;
os << "expected: " << a << ", actual: " << b << "\n";
RTITestLog_logParamString(
RTI_TEST_LOG_BIT_EXCEPTION,
"%s",
os.str().c_str());
}
template <
typename T,
typename U,
typename std::enable_if<
!std::is_floating_point<T>::value
&& !std::is_floating_point<U>::value,
bool>::type = true>
/** @internal
* @ingroup RTITestUtilsCPP
* @brief Checks if two values are equal.
* @tparam T \b In. The type of the first value.
* @tparam U \b In. The type of the second value.
* @param a \b In. The first value to compare.
* @param b \b In. The second value to compare.
* @return true if `a` is equal to `b`, false otherwise.
*/
bool check_equals(const T& a, const U& b)
{
if (a != b) {
print_assert_equals_failure(a, b);
return false;
}
return true;
}
template <
typename T,
typename U,
typename std::enable_if<
std::is_floating_point<T>::value
|| std::is_floating_point<U>::value,
bool>::type = true>
/** @internal
* @ingroup RTITestUtilsCPP
* @brief Checks if two floating point values are equal.
* @tparam T \b In. The type of the first value.
* @tparam U \b In. The type of the second value.
* @param a \b In. The first value to compare.
* @param b \b In. The second value to compare.
* @return true if `a` is equal to `b`, false otherwise.
*/
bool check_equals(const T& a, const U& b)
{
long double diff =
std::fabs(static_cast<long double>(a) - static_cast<long double>(b));
bool condition_check = diff > std::numeric_limits<double>::epsilon();
if (condition_check) {
print_assert_equals_failure(a, b);
return false;
}
return true;
}
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that two given values are equal.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
* @note This macro requires that the type provide the != and << operators.
*/
#define RTI_TEST_ASSERT_EQUALS(expected__, actual__) \
if (!check_equals(expected__, actual__)) { \
RTI_TEST_THROW(expected__ == actual__); \
}
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that two given strings are equal.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
*/
#define RTI_TEST_ASSERT_EQUALS_STRING RTI_TEST_ASSERT_EQUALS
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that two given integers are equal.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
*/
#define RTI_TEST_ASSERT_EQUALS_INT(expected__, actual__) \
RTI_TEST_ASSERT_EQUALS((int) (expected__), (int) (actual__))
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that two given long long values are equal.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
*/
#define RTI_TEST_ASSERT_EQUALS_LONG_LONG(expected__, actual__) \
RTI_TEST_ASSERT_EQUALS((long long) (expected__), (long long) (actual__))
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that two given doubles are equal.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
*/
#define RTI_TEST_ASSERT_EQUALS_DOUBLE RTI_TEST_ASSERT_EQUALS
/**
* @ingroup RTITestUtilsCPP
* @brief Sleep for a given number of seconds
* @param max_seconds__ \b In. The number of seconds to sleep.
*/
#define RTI_TEST_WAIT(max_seconds__) \
{ \
const struct RTITestTime sleepTime__ = { 0, 429496730 }; \
int sleeps__ = 0; \
while (sleeps__ < max_seconds__ * 10) { \
RTITest_sleep(&sleepTime__); \
++sleeps__; \
} \
}
/**
* @ingroup RTITestUtilsCPP
* @brief Wait for an expression to become true. Otherwise, throw a TestFailure exception.
* @param expression__ \b In. The expression to be evaluated.
* @param max_seconds__ \b In. The maximum number of seconds to wait for.
*/
#define RTI_TEST_WAIT_FOR(expression__, max_seconds__) \
{ \
/* 100ms */ \
const struct RTITestTime sleepTime__ = { 0, 429496730 }; \
int sleeps__ = 0; \
bool result__ = (expression__); \
while (!result__ && sleeps__ < max_seconds__ * 10) { \
RTITest_sleep(&sleepTime__); \
++sleeps__; \
result__ = (expression__); \
} \
if (!result__) { \
throw rti::test::TestFailure( \
"" __FILE__, \
"" RTI_TEST_TOSTRING(__LINE__), \
"wait for condition failed: " #expression__); \
} \
}
/**
* @ingroup RTITestUtilsCPP
* @brief Wait for two values to become equal. Otherwise, throw a TestFailure exception.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
* @param max_seconds__ \b In. The maximum number of seconds to wait for.
*/
#define RTI_TEST_WAIT_FOR_EQUALS(expected__, actual__, max_seconds__) \
{ \
try { \
RTI_TEST_WAIT_FOR((expected__) == (actual__), (max_seconds__)); \
} catch (const rti::test::TestFailure&) { \
print_assert_equals_failure(expected__, actual__); \
throw; \
} \
}
/**
* @ingroup RTITestUtilsCPP
* @brief Wait for a value to become equal to or greater than another one.
* Otherwise, throw a TestFailure exception.
* @param actual__ \b In. The actual value.
* @param limit__ \b In. The limit value.
* @param max_seconds__ \b In. The maximum number of seconds to wait for.
*/
#define RTI_TEST_WAIT_FOR_GREATER_OR_EQUALS(actual__, limit__, max_seconds__) \
{ \
try { \
RTI_TEST_WAIT_FOR((actual__) >= (limit__), (max_seconds__)); \
} catch (const rti::test::TestFailure&) { \
print_assert_equals_failure(limit__, actual__); \
throw; \
} \
}
/**
* @ingroup RTITestUtilsCPP
* @brief Wait for an integer to become equal to or greater than another one.
* Otherwise, throw a TestFailure exception.
* @param actual__ \b In. The actual value.
* @param limit__ \b In. The limit value.
* @param max_seconds__ \b In. The maximum number of seconds to wait for.
*/
#define RTI_TEST_WAIT_FOR_GREATER_OR_EQUALS_INT \
RTI_TEST_WAIT_FOR_GREATER_OR_EQUALS
/**
* @ingroup RTITestUtilsCPP
* @brief Wait for two given integers to become equal.
* @param expected__ \b In. The expected value.
* @param actual__ \b In. The actual value.
* @param max_seconds__ \b In. The maximum number of seconds to wait for.
*/
#define RTI_TEST_WAIT_FOR_EQUALS_INT RTI_TEST_WAIT_FOR_EQUALS
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that a given statement__ throws an exception of type exception_class__.
* Otherwise, throw a TestFailure exception.
* @param statement__ \b In. The statement to be evaluated.
* @param exception_class__ \b In. The type of exception to be thrown.
* @details Prints expected errors start and end log messages.
*/
#define RTI_TEST_ASSERT_EXCEPTION(statement__, exception_class__) \
try { \
RTITestLog_printExpectedErrorsStart(); \
statement__; \
RTI_TEST_FAIL("Expected exception not thrown"); \
} catch (const exception_class__&) { \
RTITestLog_printExpectedErrorsEnd(); \
}
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that a given statement__ throws an exception of type exception_class__.
* Otherwise, throw a TestFailure exception.
* @param statement__ \b In. The statement to be evaluated.
* @param exception_class__ \b In. The type of exception to be thrown.
* @details This macro doesn't print the expected error's start and end log messages.
*/
#define RTI_TEST_ASSERT_EXCEPTION_NO_WARN(statement__, exception_class__) \
try { \
statement__; \
RTI_TEST_FAIL("Expected exception not thrown"); \
} catch (const exception_class__&) { }
/** @internal
* @ingroup RTITestUtilsCPP
* @brief Throws a TestFailure exception if the given statement throws an exception.
* @param statement__ \b In. The statement to be evaluated.
* @param msg__ \b In. The message to be included in the exception.
*/
#define RTI_TEST_TRACE_W_MSG(statement__, msg__) \
try { \
(statement__); \
} catch (const std::exception& ex) { \
throw rti::test::TestFailure( \
"" __FILE__, \
"" RTI_TEST_TOSTRING(__LINE__), \
msg__, \
ex); \
}
/**
* @ingroup RTITestAssertionsCPP
* @brief Assert that a given value is within a given tolerance.
* @param expected__ \b In. The expected value.
* @param count__ \b In. The actual value.
* @param tolerance_percentage__ \b In. The tolerance percentage.
*/
#define RTI_TEST_ASSERT_RANGE(expected__, count__, tolerance_percentage__) \
{ \
double tolerance__ = (double) (tolerance_percentage__) / 100; \
double max_count__ = (expected__) * (1 + tolerance__); \
double min_count__ = (expected__) * (1 - tolerance__); \
try { \
RTI_TEST_ASSERT((count__) >= min_count__ && (count__) <= max_count__); \
} catch (std::exception&) { \
RTITestLog_freeForm( \
RTI_LOG_BIT_EXCEPTION, \
"%s:%d: value out of range: [%f, %f], count: %f\n", \
(__FILE__), \
(__LINE__), \
min_count__, \
max_count__, \
(double) (count__)); \
throw; \
} \
}
/** @internal
* @ingroup RTITestUtilsCPP
* @brief Throws a TestFailure exception if the given statement throws an exception.
* @param statement__ \b In. The statement to be evaluated.
* @param msg__ \b In. The message to be included in the exception.
* @details If an assertion fails within a function call in statement__,
* this macro adds this statement__ as a reason for the failure.
* This is a way to build an exception stack to log more than
* just the line where the exception originated.
*/
#define RTI_TEST_TRACE(statement__) \
RTI_TEST_TRACE_W_MSG(statement__, "test failure: " #statement__)
#endif // RTI_TEST_ASSERTIONS_HPP_