/* * (c) Copyright, Real-Time Innovations, 2002-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 rtitest_assert_h #define rtitest_assert_h #include #include #include "rtitest/test_assert_types.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup RTITestAssertion Assertions * @brief Macros to assert conditions in unit tests. */ /** * @defgroup RTITestUtilities Utilities * @brief Macros to help writing unit tests. */ /** @internal * @ingroup RTITestAssertion * @brief Define this variable to print a debug message every time * a unit test calls RTITest_assert() or RTITest_assertWithStr() */ #ifdef RTI_TEST_ENABLE_DEBUG_ASSERT #define RTITest_debugAssert(str__) \ RTITestLogger_log( \ RTI_TEST_LOG_BIT_EXCEPTION, \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[DEBUG %s:%d] executing command: %s", \ __FILE__, \ __LINE__, \ #str__) #else #define RTITest_debugAssert(str__) #endif /** @internal * @ingroup RTITestAssertion * @brief Copy a message into RTITest_g_lastAssertErrorMessage. Expects the * message as a formatted string and its parameters as variadic arguments. * * @details This macro is used in other assert macros to introduce some extra * information in the JUnit output XML. */ #define RTITest_setLastAssertErrorMessage(...) \ if (RTITest_g_lastAssertErrorMessage != NULL) { \ int unusedReturnValue__; \ unusedReturnValue__ = RTITestLog_snprintf( \ RTITest_g_lastAssertErrorMessage, \ RTI_TEST_MAX_ASSERT_ERROR_MESSAGE_SIZE, \ __VA_ARGS__); \ (void) unusedReturnValue__; \ } /** @internal * @ingroup RTITestAssertion * @brief Log an assert message and copy the message on * RTITest_g_lastAssertErrorMessage. * @param logMessage__ \b In. The message format, with its variadic arguments. */ #define RTITest_assertLog(logMessage__, ...) \ (void) METHOD_NAME; \ RTITest_setLastAssertErrorMessage(__VA_ARGS__); \ RTITestLogger_log(RTI_TEST_LOG_BIT_EXCEPTION, (logMessage__), __VA_ARGS__) /** @internal * @ingroup RTITestAssertion * @brief Log an assert message with function name and copy the message on * RTITest_g_lastAssertErrorMessage. * @param functionName__ \b In. The function name. * @param logMessage__ \b In. The message format, with its variadic arguments. */ #define RTITest_assertLogWithFunctionName(functionName__, logMessage__, ...) \ RTITest_setLastAssertErrorMessage(__VA_ARGS__); \ RTITestLogger_logWithFunctionName( \ RTI_TEST_LOG_BIT_EXCEPTION, \ (functionName__), \ (logMessage__), \ __VA_ARGS__) /** @internal * @ingroup RTITestAssertion * @brief Assert that a statement is true. Otherwise, log the given message. * @param statement__ \b In. Statement to be run and asserted. * @param str__ \b In. Message to be printed in case of failure. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertWithStr(statement__, str__) \ RTITest_debugAssert(statement__); \ if (!(statement__)) { \ const char *strConst__ = str__; \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: %s reason: %s", \ __FILE__, \ __LINE__, \ #statement__, \ strConst__); \ goto done; \ } /** * @ingroup RTITestAssertion * @brief Assert that statement__ is TRUE with a custom action * on failure * * @details If statement__ is not true, then failAction__ will * be performed */ #define RTITest_assertWithAction(statement__, failAction__) \ RTITest_debugAssert(statement__); \ if (!(statement__)) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: %s", \ __FILE__, \ __LINE__, \ #statement__); \ failAction__; \ } /** * @ingroup RTITestAssertion * @brief Run and assert a statement. If false, go to done tag. * @param statement__ \b In. Statement to be asserted. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assert(statement__) \ RTITest_assertWithAction(statement__, goto done) /** * @ingroup RTITestAssertion * @brief Run and assert a statement after the done tag. This sets the variable result to RTI_TEST_RETCODE_FAILED if the statement evaluates to false. * @param statement__ \b In. Statement to be run and asserted. */ #define RTITest_assertAfterDone(statement__) \ RTITest_assertWithAction(statement__, result = RTI_TEST_RETCODE_FAILED) /** * @ingroup RTITestAssertion * @brief Run and assert a statement. In case of failure, only prints a message, no other action is run. * @param statement__ \b In. Statement to be run and asserted. */ #define RTITest_assertWithNoAction(statement__) \ RTITest_assertWithAction(statement__,) /* * Use almost equals macros only if you cannot get the right precision. Use * regular assert equals macros instead. */ /** * @ingroup RTITestAssertion * @brief Assert that two floating point numbers are almost equal. * @param limit__ \b In. The maximum difference between the two numbers. * @param actual__ \b In. The actual value. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertAlmostEqualsFloat(limit__, actual__) \ RTITest_assertComparisonForFloatingPointTypeWithAction( \ limit__, \ actual__, \ float, \ ==, \ 0.0000007, \ goto done) /** * @ingroup RTITestAssertion * @brief Assert that two doubles are almost equal. * @param limit__ \b In. The maximum difference between the two numbers. * @param actual__ \b In. The actual value. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertAlmostEqualsDouble(limit__, actual__) \ RTITest_assertComparisonForFloatingPointTypeWithAction( \ limit__, \ actual__, \ double, \ ==, \ 0.0000007, \ goto done) /** * @ingroup RTITestAssertion * @brief Assert that two given Sequence Numbers are equal. * @param expected__ \b In. The expected Sequence Number. * @param actual__ \b In. The actual Sequence Number. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertEqualsSN(expected__, actual__) \ { \ RTITest_assertComparisonForType( \ (expected__).low, \ (actual__).low, \ unsigned int, \ "%u", \ ==); \ RTITest_assertComparisonForType( \ (expected__).high, \ (actual__).high, \ int, \ "%d", \ ==); \ } /** @internal * @ingroup RTITestAssertion * @brief Assert that an operation is true for two given DDS_Duration_t values. * @param expected__ \b In. The expected DDS_Duration_t value. * @param actual__ \b In. The actual DDS_Duration_t value. * @param operation__ \b In. The operation to be asserted. * @param failAction__ \b In. Action to be run if the assertion fails. */ #define RTITest_assertComparisonForDDS_Duration_tWithAction(expected__, actual__, operation__, failAction__) \ { \ RTITest_assertComparisonForTypeWithAction( \ expected__.sec, \ actual__.sec, \ int, \ "%d", \ operation__, \ failAction__); \ RTITest_assertComparisonForTypeWithAction( \ expected__.nanosec, \ actual__.nanosec, \ unsigned int, \ "%d", \ operation__, \ failAction__); \ } /** @internal * @ingroup RTITestAssertion * @brief Assert that an operation is true for two given DDS_Duration_t values. * @param expected__ \b In. The expected DDS_Duration_t value. * @param actual__ \b In. The actual DDS_Duration_t value. * @param operation__ \b In. The operation to be asserted. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertComparisonForDDS_Duration_t(expected__, actual__, operation__) \ RTITest_assertComparisonForDDS_Duration_tWithAction( \ expected__, \ actual__, \ operation__, \ goto done) /** * @ingroup RTITestAssertion * @brief Assert that two given DDS_Duration_t values are equal. * @param expected__ \b In. The expected DDS_Duration_t value. * @param actual__ \b In. The actual DDS_Duration_t value. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertEqualsDDS_Duration_t(expected__, actual__) \ RTITest_assertComparisonForDDS_Duration_t(expected__, actual__, ==) /** * @ingroup RTITestAssertion * @brief Assert that two given strings are equal. * @param expected__ \b In. The expected string. * @param actual__ \b In. The actual string. * @pre expected__ != NULL && actual__ != NULL * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertEqualsString(expected__, actual__) \ RTITest_debugAssert(strcmp((expected__), (actual__)) == 0); \ if (strcmp((expected__), (actual__)) != 0) { \ const char *expectedConst__ = expected__; \ const char *actualConst__ = actual__; \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: strcmp(%s, %s) == 0: expected: %s, " \ "actual: %s", \ __FILE__, \ __LINE__, \ #expected__, \ #actual__, \ expectedConst__, \ actualConst__); \ goto done; \ } /** * @ingroup RTITestAssertion * @brief Assert that two given strings are not equal. * @param expected__ \b In. The expected string. * @param actual__ \b In. The actual string. * @pre expected__ != NULL && actual__ != NULL * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertNotEqualsString(expected__, actual__) \ RTITest_debugAssert(strcmp((expected__), (actual__)) != 0); \ if (strcmp((expected__), (actual__)) == 0) { \ const char *expectedConst__ = expected__; \ const char *actualConst__ = actual__; \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: strcmp(%s, %s) != 0: not expected: %s, " \ "actual: %s", \ __FILE__, \ __LINE__, \ #expected__, \ #actual__, \ expectedConst__, \ actualConst__); \ goto done; \ } /** * @ingroup RTITestAssertion * @brief Assert that expectedSubStr__ is contained in actualStr__. * @param expectedSubStr__ \b In. The substring to be searched. * @param actualStr__ \b In. The string to be scanned. * * @details Example: RTITest_assertSubString("a","abc"); * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertSubString(expectedSubStr__, actualStr__) \ RTITest_debugAssert(strstr((actualStr__), (expectedSubStr__)) == NULL); \ if (strstr((actualStr__), (expectedSubStr__)) == NULL) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: expected to find string %s in %s", \ __FILE__, \ __LINE__, \ expectedSubStr__, \ actualStr__); \ goto done; \ } /** * @ingroup RTITestAssertion * @brief Assert that a given object is not NULL. * @param object__ \b In. The object to be asserted. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertNotNull(object__) \ RTITest_assertNotNullWithAction(object__, goto done) /** * @ingroup RTITestAssertion * @brief Assert that a given object is not NULL. * @param object__ \b In. The object to be asserted. * @param failAction__ \b In. Action to be run if the assertion fails. */ #define RTITest_assertNotNullWithAction(object__, failAction__) \ RTITest_debugAssert((object__) != NULL); \ if ((object__) == NULL) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] pointer is null: %s", \ __FILE__, \ __LINE__, \ #object__); \ failAction__; \ } /** * @ingroup RTITestAssertion * @brief Assert that a given object is NULL. * @param object__ \b In. The object to be asserted. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertNull(object__) \ RTITest_assertNullWithAction(object__, goto done) /** * @ingroup RTITestAssertion * @brief Assert that a given object is NULL. * @param object__ \b In. The object to be asserted. * @param failAction__ \b In. Action to be run if the assertion fails. */ #define RTITest_assertNullWithAction(object__, failAction__) \ RTITest_debugAssert((object__) == NULL); \ if ((object__) != NULL) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] pointer is not null: %s", \ __FILE__, \ __LINE__, \ #object__); \ failAction__; \ } /** * @ingroup RTITestAssertion * @brief Assert that two given DDS_ReturnCode_t return codes are equal. * @param retcode__ \b In. The actual DDS_ReturnCode_t return code. * @param failAction__ \b In. Action to be run if the assertion fails. * @param expectedRetcode__ \b In. The expected DDS_ReturnCode_t return code. */ #define RTITest_assertRetcodeWithAction( \ retcode__, \ failAction__, \ expectedRetcode__) \ { \ DDS_ReturnCode_t retCodeTmp__ = (retcode__); \ RTITest_debugAssert(retCodeTmp__ == (expectedRetcode__)); \ if (retCodeTmp__ != (expectedRetcode__)) { \ if ((int) retCodeTmp__ < 0) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: %s == %s", \ __FILE__, \ __LINE__, \ #retcode__, \ #expectedRetcode__); \ } else { \ unsigned int retCodeAsUInt__ = (unsigned int) retCodeTmp__; \ unsigned int index__ = \ (retCodeAsUInt__ < RTI_TEST_NUM_RETCODES - 1 \ ? retCodeAsUInt__ \ : RTI_TEST_NUM_RETCODES); \ index__ = \ (retCodeTmp__ == DDS_RETCODE_NOT_ALLOWED_BY_SECURITY \ ? RTI_TEST_NUM_RETCODES - 1 \ : index__); \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: %s == %s: got %s", \ __FILE__, \ __LINE__, \ #retcode__, \ #expectedRetcode__, \ RTITest_g_returnCodeStrings[index__]); \ } \ failAction__; \ } \ } /** * @ingroup RTITestAssertion * @brief Assert that a given DDS_ReturnCode_t return code is DDS_RETCODE_OK. * Otherwise, run the given action. * @param retcode__ \b In. The actual DDS_ReturnCode_t return code. * @param failAction__ \b In. Action to be run if the assertion fails. */ #define RTITest_assertOkWithAction(retcode__, failAction__) \ RTITest_assertRetcodeWithAction(retcode__, failAction__, DDS_RETCODE_OK) /** * @ingroup RTITestAssertion * @brief Assert that a given DDS_ReturnCode_t return code is DDS_RETCODE_OK. * @param retcode__ \b In. The actual DDS_ReturnCode_t return code. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertOk(retcode__) \ RTITest_assertOkWithAction((retcode__), goto done) #define RTITest_assertPassed(retcode__) \ RTITest_assertEqualsInt(RTI_TEST_RETCODE_PASSED, (retcode__)) /** * @ingroup RTITestAssertion * @brief Assert that a given DDS_ReturnCode_t return code is DDS_RETCODE_ERROR. * Otherwise, run the given action. * @param retcode__ \b In. The actual DDS_ReturnCode_t return code. * @param failAction__ \b In. Action to be run if the assertion fails. */ #define RTITest_assertErrorWithAction(retcode__, failAction__) \ RTITest_assertRetcodeWithAction(retcode__, failAction__, DDS_RETCODE_ERROR) /** * @ingroup RTITestAssertion * @brief Assert that two DDS_ReturnCode_t return codes are equal, otherwise * goto done. This macro requires the done label to be defined. * @param expected__ \b In. The expected DDS_ReturnCode_t. * @param retcode__ \b In. The actual DDS_ReturnCode_t. */ #define RTITest_assertEqualsRetcode(expected__, retcode__) \ RTITest_assertRetcodeWithAction(retcode__, goto done, expected__) /** * @ingroup RTITestAssertion * @brief Assert that a given DDS_ReturnCode_t return code is DDS_RETCODE_ERROR. * @param retcode__ \b In. The actual DDS_ReturnCode_t return code. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertError(retcode__) \ RTITest_assertErrorWithAction((retcode__), goto done) /* * Use after the done label to set the test result to failed if the assert fails * requires the test result variable to be named 'result' */ /** * @ingroup RTITestAssertion * @brief Assert that a given DDS_ReturnCode_t return code is DDS_RETCODE_OK. * Otherwise, set the result variable to RTI_TEST_RETCODE_FAILED. * @param retcode__ \b In. The actual DDS_ReturnCode_t return code. */ #define RTITest_assertOkAfterDone(retcode__) \ RTITest_assertOkWithAction((retcode__), result = RTI_TEST_RETCODE_FAILED) /** * @ingroup RTITestAssertion * @brief Assert that a given statement is false. * Otherwise, run the given action */ #define RTITest_assertFalseWithAction(statement__, failAction__) \ RTITest_debugAssert(statement__); \ if ((statement__)) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "[%s:%d] assertion failed: !%s", \ __FILE__, \ __LINE__, \ #statement__); \ failAction__; \ } /** * @ingroup RTITestAssertion * @brief Assert that a given statement is false. * @param statement__ \b In. The statement to be asserted. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertFalse(statement__) \ RTITest_assertFalseWithAction(statement__, goto done) /** * @ingroup RTITestAssertion * @brief Run and assert a statement after the done tag. This sets the variable result to RTI_TEST_RETCODE_FAILED if the statement evaluates to true. * @param statement__ \b In. Statement to be run and asserted. */ #define RTITest_assertFalseAfterDone(statement__) \ RTITest_assertFalseWithAction(statement__, result = RTI_TEST_RETCODE_FAILED) /** * @ingroup RTITestAssertion * @brief Assert that a given value is within a given tolerance. * Otherwise, run the given action. * @param expected__ \b In. The expected value. * @param count__ \b In. The actual value. * @param tolerance_percentage__ \b In. The tolerance percentage. * @param failAction__ \b In. Action to be run if the assertion fails. * @details Checks whether count__ is within a margin specified by expected__ and a * tolerance_percentage__ */ #define RTITest_assertMarginWithAction(expected__, count__, tolerance_percentage__, failAction__) \ { \ double tolerance = (double) (tolerance_percentage__) / 100; \ double max_count = (double) (expected__) * (1 + tolerance); \ double min_count = (double) (expected__) * (1 - tolerance); \ if (!((double) (count__) >= min_count \ && (double) (count__) <= max_count)) { \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "%s:[%s:%d]: value out of range: [%f, %f], count: %f\n", \ RTI_FUNCTION_NAME, \ __FILE__, \ __LINE__, \ min_count, \ max_count, \ (double) (count__)); \ failAction__; \ } \ } /** * @ingroup RTITestAssertion * @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. * @details Checks whether count__ is within a margin specified by expected__ and a * tolerance_percentage__. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_assertMargin(expected__, count__, tolerance_percentage__) \ RTITest_assertMarginWithAction( \ expected__, \ count__, \ tolerance_percentage__, \ goto done) /** * @ingroup RTITestUtilities * @brief Make a test fail with a given message. * @param msg__ \b In. The message to be printed. * @details Calls `goto done` if the assertion fails. Therefore, this macro requires a `done` label to be defined in the method that this assertion is called in. */ #define RTITest_fail(msg__) \ RTITest_assertLog( \ &RTI_TEST_LOG_EMPTY_TEMPLATE, \ "%s[%s:%d] %s\n", \ RTI_FUNCTION_NAME, \ __FILE__, \ __LINE__, \ msg__); \ goto done; /** @internal * @ingroup RTITestUtilities * @brief True if Simple Participant Discovery Protocol 2 is enabled, false otherwise. * @param participantQos__ \b In. The participant QoS. */ #define RTITest_isSpdp2Enabled(participantQos__) \ (participantQos__.discovery_config.builtin_discovery_plugins \ & DDS_DISCOVERYCONFIG_BUILTIN_SPDP2) #ifdef __cplusplus } /* extern "C" */ #endif #endif /* rtitest_assert_h */