Files
qt6-migration-pipeline/external/rti_connext_dds-7.7.0/include/ndds/rtitest/Tester.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

559 lines
18 KiB
C++

/*
* (c) Copyright, Real-Time Innovations, 2014-2022.
*
* 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_TESTER_HPP_
#define RTI_TEST_TESTER_HPP_
#include <stdio.h>
#include <stdlib.h>
#include <stdexcept>
#include <string>
#include <functional> // for std::mem_fun
#include <vector>
#include "rtitest/test_setting.h"
#include "rtitest/TestAssertion.hpp"
/**
* @mainpage Unit Test Framework (Modern C++)
* @brief Modern C++ wrapper of Unit Test Framework.
*/
/**
* @defgroup RTITestCPPClasses Test definitions
* @brief Implements Unit Test Framework Modern C++ API. See the
* <a href="../../../manuals/unit_test_framework_users_manual/index.html">user's
* manual</a> for examples of how to use most of these APIs.
*/
namespace rti { namespace test {
// This adapter function takes a member function of a C++ Tester and runs it as
// the C test framework expects: it catches exceptions, reports errors as
// return codes and prints log info.
template <typename Tester, typename TestFunc>
int run_test_function(
const struct RTITestSetting *setting,
const std::string& function_name,
Tester& tester,
TestFunc test_function)
{
// Log the test that is running
RTITestLog_printTestNameExtWithFunctionName(function_name.c_str(), NULL);
// Set the current test name in the global variable
if (RTITest_g_currentTestNameAllocated != NULL) {
size_t pos = function_name.find("::");
if (pos == std::string::npos) {
if (function_name.size()
< RTI_TEST_MAX_CURRENT_TEST_NAME_ALLOCATED_SIZE - 1) {
RTITest_g_currentTestNameAllocated =
strncpy(RTITest_g_currentTestNameAllocated,
function_name.c_str(),
function_name.size());
}
} else {
size_t method_name_size = pos + strlen("::");
if (function_name.size() - method_name_size
< RTI_TEST_MAX_CURRENT_TEST_NAME_ALLOCATED_SIZE - 1) {
RTITest_g_currentTestNameAllocated =
strncpy(RTITest_g_currentTestNameAllocated,
function_name.c_str() + method_name_size,
function_name.size() - method_name_size);
}
}
}
try {
tester.set_settings(setting);
if (tester.file_system_is_required()) {
tester.assert_file_system_support();
}
// Run the actual test
test_function(&tester);
} catch (const TestFailure& ex) {
// Test fails because an assertion failed
RTITest_assertLogWithFunctionName(
function_name.c_str(),
&RTI_TEST_LOG_EMPTY_TEMPLATE,
"%s",
ex.what());
if (RTITestConnext_isAbortOnErrorEnabled()) {
throw;
} else {
return 0;
}
} catch (const TestIncomplete& ex) {
// Test fails because an assertion failed
return ex.retcode();
} catch (const std::exception& ex) {
// Test fails because some operation failed (not an assertion)
RTITest_assertLogWithFunctionName(
function_name.c_str(),
&RTI_TEST_LOG_EMPTY_TEMPLATE,
"Unhandled exception in test: %s",
ex.what());
if (RTITestConnext_isAbortOnErrorEnabled()) {
throw;
} else {
return 0;
}
} catch (...) {
// Test fails because of an unknown exception
RTITest_assertLogWithFunctionName(
function_name.c_str(),
&RTI_TEST_LOG_EMPTY_TEMPLATE,
"Unknown exception in test");
if (RTITestConnext_isAbortOnErrorEnabled()) {
throw;
} else {
return 0;
}
}
return 1; // test passes
}
/**
* @ingroup RTITestCPPClasses
* @brief This macro generates and registers a function that the C test
* framework can run. This macro must be called in a Tester constructor.
*/
#define RTI_TEST_FUNCTION_ADD(class_name__, test_function__) \
struct runner_##test_function__ { \
static int run(const struct RTITestSetting *setting) \
{ \
return run_test_function( \
setting, \
class_name__::get_instance().get_name() + "::" #test_function__, \
class_name__::get_instance(), \
std::mem_fn(&class_name__::test_function__)); \
} \
}; \
\
add_test_function((RTITestFunction) runner_##test_function__::run)
// The RTI_TEST_FUNCTION_ADD_ADDITIONAL macro was created to support splitting
// tests as basic and additional. Tests added with this macro will belong to
// the additional test set
#define RTI_TEST_FUNCTION_ADD_ADDITIONAL(class_name__, test_function__) \
struct runner_##test_function__ { \
static int run(const struct RTITestSetting *setting) \
{ \
return run_test_function( \
setting, \
class_name__::get_instance().get_name() + "::" #test_function__, \
class_name__::get_instance(), \
std::mem_fn(&class_name__::test_function__)); \
} \
}; \
\
add_test_function_additional((RTITestFunction) runner_##test_function__::run)
// The RTI_TEST_FUNCTION_ADD_BASICPUBSUB macro was created to support running a
// basic publish subscribe test as part of
// CORE_BASICPUBSUB_VALIDATION_ARCHITECTURES. Tests added with this macro will
// belong to _test_functions_basic_pubsub test set
#define RTI_TEST_FUNCTION_ADD_BASICPUBSUB(class_name__, test_function__) \
struct runner_basicpubsub_##test_function__ { \
static int run(const struct RTITestSetting *setting) \
{ \
return run_test_function( \
setting, \
class_name__::get_instance().get_name() + "::" #test_function__, \
class_name__::get_instance(), \
std::mem_fn(&class_name__::test_function__)); \
} \
}; \
\
add_test_function_basic_pubsub( \
(RTITestFunction) runner_basicpubsub_##test_function__::run)
#define RTI_TEST_FUNCTION_ADD_FROM_BASE_CLASS( \
CLASS_NAME, BASE_CLASS, TEST_FUNCTION) \
struct runner_ ## TEST_FUNCTION { \
static int run(const struct RTITestSetting* setting) \
{ \
return rti::test::run_test_function( \
setting, \
CLASS_NAME::get_instance().get_name()+"::"#TEST_FUNCTION, \
static_cast<BASE_CLASS&>(CLASS_NAME::get_instance()), \
std::mem_fn(&BASE_CLASS::TEST_FUNCTION)); \
} \
}; \
\
add_test_function((RTITestFunction) runner_ ## TEST_FUNCTION::run);
/*
Inherit from this class to get a singleton behavior:
class MyClass : public Singleton<MyClass> {
private:
MyClass();
friend Singleton<MyClass>;
};
*/
template <class T>
class Singleton {
public:
static T& get_instance()
{
static T instance;
return instance;
}
};
/**
* @brief Auxiliary class that augments C structs with a destructor.
*
* @param T The type of the struct to augment.
* @param R1 The return type of the initializer function.
* @param InitializeFn The function to call when the struct is created.
* @param R2 The return type of the finalizer function.
* @param FinalizeFn The function to call when the struct is destroyed.
*
* @details This class is used to wrap C structs that have a constructor and
* destructor function. The constructor will call the provided initializer
* function, and the destructor will call the provided finalizer function.
* The struct must be trivially destructible.
*/
template <
typename T,
typename R1,
R1 InitializeFn(T *),
typename R2,
R2 FinalizeFn(T *)>
struct RaiiWrapper : public T {
RaiiWrapper()
{
(void) InitializeFn(this);
}
~RaiiWrapper()
{
(void) FinalizeFn(this);
}
RaiiWrapper(RaiiWrapper const&) = delete;
RaiiWrapper(RaiiWrapper&&) = delete;
RaiiWrapper& operator=(RaiiWrapper const&) = delete;
RaiiWrapper& operator=(RaiiWrapper&&) = delete;
};
/* Provides basic functionality all Testers can benefit from:
* Registration of test functions
* Running those test functions using the underlying test framework
* Accessing the test settings
* Indicating that a test requires file system--that will ignore all tests
if the current platform doesn't have one.
*/
class Tester {
public:
Tester(const std::string& name,
RTITestLevel test_level = RTI_TEST_LEVEL_UNIT,
bool tests_require_file_system = false)
: _settings(NULL),
_name(name),
_test_level(test_level),
_tests_require_file_system(tests_require_file_system)
{ }
virtual ~Tester()
{ }
const std::string& get_name() const
{
return _name;
}
// Methods to register and run tests:
int get_test_count() const
{
return static_cast<int>(_test_functions.size());
}
int get_test_count_additional() const
{
return static_cast<int>(_test_functions_additional.size());
}
int get_test_count_basic_pubsub() const
{
return static_cast<int>(_test_functions_basic_pubsub.size());
}
const RTITestFunction *get_test_functions() const
{
return (_test_functions.empty() ? NULL : &_test_functions[0]);
}
const RTITestFunction *get_test_functions_additional() const
{
return (_test_functions_additional.empty()
? NULL
: &_test_functions_additional[0]);
}
const RTITestFunction *get_test_functions_basic_pubsub() const
{
return (_test_functions_basic_pubsub.empty()
? NULL
: &_test_functions_basic_pubsub[0]);
}
void add_test_function(RTITestFunction fnc)
{
_test_functions.push_back(fnc);
}
void add_test_function_additional(RTITestFunction fnc)
{
_test_functions_additional.push_back(fnc);
}
void add_test_function_basic_pubsub(RTITestFunction fnc)
{
_test_functions_basic_pubsub.push_back(fnc);
}
template <typename SingletonTester>
void merge_tester()
{
merge_tester(SingletonTester::get_instance());
}
void set_settings(const RTITestSetting *settings)
{
_settings = settings;
}
const RTITestSetting *get_settings() const
{
if (_settings == NULL) {
throw std::logic_error("test settings not specified");
}
return _settings;
}
std::string get_resource_path(const std::string& file) const
{
char resourcePath[RTI_TEST_MAX_FILE_NAME_LENGTH] = { 0 };
RTI_TEST_ASSERT(RTITestSetting_getFullResourcePathFileName(
get_settings(),
resourcePath,
file.c_str()));
return std::string(resourcePath);
}
int32_t domain_id() const
{
return get_settings()->id;
}
bool file_system_is_required() const
{
return _tests_require_file_system;
}
bool file_system_support() const
{
return (RTITestSetting_fileTestIsSupport(get_settings()) != 0);
}
void assert_file_system_support() const
{
if (!file_system_support()) {
RTI_TEST_THROW_UNSUPPORTED("File system not supported");
}
}
virtual RTIBool run_tests(const RTITestSetting *settings)
{
int begin, end;
RTITestFunction *testSelectionBasic = NULL,
*testSelectionAdditional = NULL;
int testSelectionBasicSize = 0, testSelectionAdditionalSize = 0;
RTITestSetting_getRange(settings, &begin, &end, _test_level);
if (RTITestSetting_isCoreBasicPubSubValidationArchitecture()
&& !RTITestSetting_isRangeSpecified(begin, end)) {
testSelectionBasic =
(RTITestFunction *) get_test_functions_basic_pubsub();
testSelectionBasicSize = get_test_count_basic_pubsub();
testSelectionAdditional = NULL;
testSelectionAdditionalSize = 0;
} else {
testSelectionBasic = (RTITestFunction *) get_test_functions();
testSelectionBasicSize = get_test_count();
testSelectionAdditional =
(RTITestFunction *) get_test_functions_additional();
testSelectionAdditionalSize = get_test_count_additional();
}
return RTITestSetting_runTestsExt(
settings,
_name.c_str(),
testSelectionBasic,
testSelectionBasicSize,
testSelectionAdditional,
testSelectionAdditionalSize,
begin,
end);
}
private:
// Adds the test functions from src_tester into dest_tester.
// Useful to combine smaller Testers into a larger one
void merge_tester(Tester& src_tester)
{
const RTITestFunction *src_functions = src_tester.get_test_functions();
const int test_count = src_tester.get_test_count();
const RTITestFunction *src_functions_additional =
src_tester.get_test_functions_additional();
const int test_count_additional =
src_tester.get_test_count_additional();
const RTITestFunction *src_functions_basic_pubsub =
src_tester.get_test_functions_basic_pubsub();
const int test_count_basicpubsub =
src_tester.get_test_count_basic_pubsub();
if (src_functions != NULL) {
for (int i = 0; i < test_count; i++) {
add_test_function(src_functions[i]);
}
}
if (src_functions_additional != NULL) {
for (int i = 0; i < test_count_additional; i++) {
add_test_function_additional(src_functions_additional[i]);
}
}
if (src_functions_basic_pubsub != NULL) {
for (int i = 0; i < test_count_basicpubsub; i++) {
add_test_function_basic_pubsub(src_functions_basic_pubsub[i]);
}
}
}
const RTITestSetting *_settings;
enum { MAX_TEST_COUNT = 64 };
std::vector<RTITestFunction> _test_functions;
std::vector<RTITestFunction> _test_functions_additional;
std::vector<RTITestFunction> _test_functions_basic_pubsub;
const std::string _name;
RTITestLevel _test_level;
bool _tests_require_file_system;
};
// Generic function to run all the test functions in a Tester.
// When instantiated for a concrete Tester it has the
// signature that the C test framework expects.
template <typename SingletonTester>
int run_tester(const struct RTITestSetting *setting)
{
return SingletonTester::get_instance().run_tests(setting); // does not
// throw
}
/**
* @ingroup RTITestCPPClasses
* @brief A container of Testers. This is the top level tester, so tester
* classes should inherits from this class.
*/
class TesterContainer {
public:
TesterContainer(const std::string& name)
: tester_(name, RTI_TEST_LEVEL_SUBMODULE)
{ }
virtual ~TesterContainer()
{
// Empty virtual destructor added because this class includes virtual
// functions
}
template <typename Tester>
void add_tester()
{
tester_.add_test_function(run_tester<Tester>);
}
template <typename Tester>
void add_tester_basic_pubsub()
{
tester_.add_test_function_basic_pubsub(run_tester<Tester>);
}
/**
* @brief Runs the tests.
*
* @param setting \b In. The RTITestSetting object that contains the test configuration.
* @return int The exit code of the test run. 0 if successful, 1 otherwise.
*/
int run_tests(const RTITestSetting& setting)
{
if (!on_tests_begin(setting)) {
return 1;
}
int ok = tester_.run_tests(&setting);
if (!on_tests_end(setting)) {
ok = 0;
}
RTITestSetting_printComplete(tester_.get_name().c_str());
return ok ? 0 : 1; // return 0 on success as expected by main()
}
/**
* @brief Parses the command-line arguments and runs the tests.
*
* @param argc \b In. The number of command-line arguments.
* @param argv \b In. The command-line arguments.
* @return The exit code of the test execution. 0 if successful, 1 otherwise.
*/
int run_tests(int argc, char **argv)
{
RTITestSetting setting;
RTITestSetting_init(&setting);
if (!RTITestSetting_parseArgument(&setting, argc, argv)) {
return RTI_FALSE;
}
return run_tests(setting);
}
protected:
// These function can be overridden to add additional behavior before and
// after running all tests.
virtual bool on_tests_begin(const RTITestSetting&)
{
return true;
}
virtual bool on_tests_end(const RTITestSetting&)
{
return true;
}
private:
Tester tester_; // does most of the work
};
}} // namespace rti::test
#endif // RTI_TEST_TESTER_HPP_