- 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
525 lines
17 KiB
C++
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_
|