From 0f7aaf0b1cf261b6c86461bb0cfbc97f4fefb05b Mon Sep 17 00:00:00 2001 From: Jacob Hageman Date: Mon, 11 May 2020 12:39:01 -0400 Subject: [PATCH] Fix #491, Add UT macros --- fsw/cfe-core/unit-test/ut_support.c | 142 +++++++++++++++ fsw/cfe-core/unit-test/ut_support.h | 257 ++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+) diff --git a/fsw/cfe-core/unit-test/ut_support.c b/fsw/cfe-core/unit-test/ut_support.c index f72c07445..bbca578d3 100644 --- a/fsw/cfe-core/unit-test/ut_support.c +++ b/fsw/cfe-core/unit-test/ut_support.c @@ -693,3 +693,145 @@ uint32 UT_PrintfIsInHistory(const char *MsgToSearchFor) return UT_GetMessageCount(MsgToSearchFor, &UT_PrintfBuffer, UT_StrCmpFormatStr); } +int32 TestStat; + +void UT_STARTBLOCK_impl(const char *FileName, int LineNum, const char *TestName) +{ +#ifdef VERBOSE + char cMsg[UT_MAX_MESSAGE_LENGTH]; + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Start Block", TestName, FileName, LineNum); + UT_Text(cMsg); +#endif /* VERBOSE */ +} + +void UT_START_impl(const char *FileName, int LineNum, const char *TestName) +{ +#ifdef VERBOSE + char cMsg[UT_MAX_MESSAGE_LENGTH]; + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Start", TestName, FileName, LineNum); + UT_Text(cMsg); +#endif /* VERBOSE */ + + TestStat = CFE_PASS; +} + +void UT_REPORT_impl(const char *FileName, int LineNum, const char *TestName) +{ + UT_Report(FileName, LineNum, TestStat, TestName, "Report"); +} + +bool UT_SETUP_impl(const char *FileName, int LineNum, const char *TestName, const char *FnName, int32 FnRet) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + if(FnRet != CFE_SUCCESS) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Setup failed (%s returned %ld)", + TestName, FileName, LineNum, FnName, (long int)FnRet); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +bool UT_ASSERT_impl(const char *FileName, int LineNum, const char *TestName, const char *FnName, int32 FnRet) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + if(FnRet != CFE_SUCCESS) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Assert failed (%s returned %ld)", + TestName, FileName, LineNum, FnName, (long int)FnRet); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +bool UT_ASSERT_EQ_impl(const char *FileName, int LineNum, const char *TestName, + const char *FnName, int32 FnRet, const char *ExpName, int32 Exp) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + if(FnRet != Exp) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Assert failed (%s [%ld] != %s[%ld])", + TestName, FileName, LineNum, FnName, (long int)FnRet, ExpName, (long int)Exp); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +bool UT_ASSERT_TRUE_impl(const char *FileName, int LineNum, const char *TestName, + const char *ExpName, bool Exp) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + if(!Exp) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Assert failed (%s)", + TestName, FileName, LineNum, ExpName); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +bool UT_EVTCNT_impl(const char *FileName, int LineNum, const char *TestName, int32 CntExp) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + int32 CntSent = UT_GetNumEventsSent(); + if(CntSent != CntExp) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Event count (sent: %ld != expected: %ld)", + TestName, FileName, LineNum, (long int)CntSent, (long int)CntExp); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +bool UT_EVTSENT_impl(const char *FileName, int LineNum, const char *TestName, const char *EvtName, int32 EvtId) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + if(!UT_EventIsInHistory(EvtId)) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Event not sent (%s [%ld])", + TestName, FileName, LineNum, EvtName, (long int)EvtId); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +bool UT_TEARDOWN_impl(const char *FileName, int LineNum, const char *TestName, const char *FnName, int32 FnRet) +{ + char cMsg[UT_MAX_MESSAGE_LENGTH]; + + if(FnRet != CFE_SUCCESS) + { + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) Teardown failed (%s returned %ld)", + TestName, FileName, LineNum, FnName, (long int)FnRet); + UT_Text(cMsg); + TestStat = CFE_FAIL; + return false; + } + return true; +} + +void UT_ENDBLOCK_impl(const char *FileName, int LineNum, const char *TestName) +{ +#ifdef VERBOSE + snprintf(cMsg, UT_MAX_MESSAGE_LENGTH, "%s (%s:%d) End Block", TestName, FileName, LineNum); + UT_Text(); +#endif /* VERBOSE */ +} diff --git a/fsw/cfe-core/unit-test/ut_support.h b/fsw/cfe-core/unit-test/ut_support.h index 1c8d8b1bd..25afc12df 100644 --- a/fsw/cfe-core/unit-test/ut_support.h +++ b/fsw/cfe-core/unit-test/ut_support.h @@ -687,4 +687,261 @@ void UT_CheckForOpenSockets(void); ******************************************************************************/ CFE_ES_ResetData_t *UT_GetResetDataPtr(void); +/*****************************************************************************/ +/** +** \brief Global representing the current state of a unit test function. +** +** \par Description +** This global variable is used by the test macros to track whether +** the current test is succeeding or has failed. Many of the macros +** fall through if this is not set to CFE_PASS (usually TestStat +** is set to CFE_PASS by the START() macro and is changed to +** CFE_FAIL by any test step [setup/assert/teardown] that fails). +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #CFE_PASS, #CFE_FAIL, #START, #REPORT, #SETUP, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE +** \sa #EVTCNT, #EVTSENT, #TEARDOWN +** +******************************************************************************/ +extern int32 TestStat; + +/*****************************************************************************/ +/** +** \brief Test is currently passing. +** +** \sa #TestStat, #CFE_FAIL +** +******************************************************************************/ +#define CFE_PASS 1 + +/*****************************************************************************/ +/** +** \brief Test has failed. +** +** \sa #TestStat, #CFE_PASS +** +******************************************************************************/ +#define CFE_FAIL 0 + +/** \brief Function to be called by the STARTBLOCK() macro */ +void UT_STARTBLOCK_impl(const char *FileName, int LineNum, const char *TestName); + +/*****************************************************************************/ +/** +** \brief Start a block of tests. +** +** \par Description +** Macro to be called at the start of a test block (a test function +** comprised of calls to multiple individual test functions.) +** +** Does nothing, generates a text entry if VERBOSE is defined. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #ENDBLOCK +** +******************************************************************************/ +#define STARTBLOCK() UT_STARTBLOCK_impl(__FILE__, __LINE__, __func__) + +/** \brief Function to be called by the START() macro */ +void UT_START_impl(const char *FileName, int LineNum, const char *TestName); + +/*****************************************************************************/ +/** +** \brief Start an individual test. +** +** \par Description +** Macro to be called at the start of an individual test. This sets the +** TestStat variable to be CFE_PASS, and generates a text message if +** VERBOSE is defined. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #SETUP, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define START() UT_START_impl(__FILE__, __LINE__, __func__) + +/** \brief Function to be called by the REPORT() macro */ +void UT_REPORT_impl(const char *FileName, int LineNum, const char *TestName); + +/*****************************************************************************/ +/** +** \brief Report the result of a test. +** +** \par Description +** This reports whether the test passed or failed (as maintained by the +** TestStat global variable.) Call this at the end of every test. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #TEARDOWN +** +******************************************************************************/ +#define REPORT() UT_REPORT_impl(__FILE__, __LINE__, __func__) + +/** \brief Function to be called by the SETUP() macro */ +bool UT_SETUP_impl(const char *FileName, int LineNum, const char *TestName, const char *FnName, int32 FnRet); + +/*****************************************************************************/ +/** +** \brief Checks the successful execution of a setup function. +** +** \par Description +** Many tests require a number of steps of setup to configure CFE such +** that the actual test can be performed. Failure of any setup steps +** result in a text message and the test being considered failed. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define SETUP(FN) (TestStat == CFE_PASS ? UT_SETUP_impl(__FILE__, __LINE__, __func__, (#FN), (FN)) : false) + +/** \brief Function to be called by the ASSERT() macro */ +bool UT_ASSERT_impl(const char *FileName, int LineNum, const char *TestName, const char *FnName, int32 FnRet); + +/*****************************************************************************/ +/** +** \brief Asserts the nominal execution of the function being tested. +** +** \par Description +** The core of each unit test is the execution of the function being tested. +** This function and macro should be used to test the nominal execution of the +** function; the expectation is that it will return CFE_SUCCESS. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT_EQ, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define ASSERT(FN) (TestStat == CFE_PASS ? UT_ASSERT_impl(__FILE__, __LINE__, __func__, (#FN), (FN)) : false) + +/** \brief Function to be called by the ASSERT_EQ() macro */ +bool UT_ASSERT_EQ_impl(const char *FileName, int LineNum, const char *TestName, + const char *FnName, int32 FnRet, const char *ExpName, int32 Exp); + +/*****************************************************************************/ +/** +** \brief Asserts the expected execution of the function being tested. +** +** \par Description +** The core of each unit test is the execution of the function being tested. +** This function and macro should be used to test the execution of the function +** and comparing the return status against the expected return status specified, +** when the return status expected is not CFE_SUCCESS. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define ASSERT_EQ(FN,EXP) (TestStat == CFE_PASS ? UT_ASSERT_EQ_impl(__FILE__, __LINE__, __func__, (#FN), (FN), (#EXP), (EXP)) : false) + +/** \brief Function to be called by the ASSERT_EQ() macro */ +bool UT_ASSERT_TRUE_impl(const char *FileName, int LineNum, const char *TestName, + const char *ExpName, bool Exp); + +/*****************************************************************************/ +/** +** \brief Asserts the expected execution of the function being tested. +** +** \par Description +** The core of each unit test is the execution of the function being tested. +** This function and macro should be used to test the execution of the function +** and comparing the return status against the expected return status specified, +** when the return status expected is not CFE_SUCCESS. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define ASSERT_TRUE(EXP) (TestStat == CFE_PASS ? UT_ASSERT_TRUE_impl(__FILE__, __LINE__, __func__, (#EXP), (EXP)) : false) + +/** \brief Function to be called by the EVTCNT() macro */ +bool UT_EVTCNT_impl(const char *FileName, int LineNum, const char *TestName, int32 CntExp); + +/*****************************************************************************/ +/** +** \brief Ensures that the test generated the expected number of events. +** +** \par Description +** Most tests will generate a number of events, and the number generated +** should be checked via this macro. If the number of events is different, +** the test is considered to have failed and an error is reported. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE, #EVTSENT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define EVTCNT(EXP) (TestStat == CFE_PASS ? UT_EVTCNT_impl(__FILE__, __LINE__, __func__, (EXP)) : false) + +/** \brief Function to be called by the EVTSENT() macro */ +bool UT_EVTSENT_impl(const char *FileName, int LineNum, const char *TestName, const char *EvtName, int32 EvtId); + +/*****************************************************************************/ +/** +** \brief Ensures that the test generated the expected event. +** +** \par Description +** Most tests will generate a number of events, and this function and macro check whether an +** event was generated. If not, the test is considered to have failed and an error is reported. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE, #EVTCNT, #REPORT, #TEARDOWN +** +******************************************************************************/ +#define EVTSENT(EVT) (TestStat == CFE_PASS ? UT_EVTSENT_impl(__FILE__, __LINE__, __func__, (#EVT), (EVT)) : false) + +/** \brief Function to be called by the TEARDOWN() macro */ +bool UT_TEARDOWN_impl(const char *FileName, int LineNum, const char *TestName, const char *FnName, int32 FnRet); +/*****************************************************************************/ +/** +** \brief Checks the successful execution of a teardown function. +** +** \par Description +** Many tests require a number of steps of setup to configure CFE such that the actual test +** can be performed, and undoing that configuration is the role of the teardown steps. Failure +** of any teardown steps result in a text message and the test being considered failed. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #START, #SETUP, #ASSERT, #ASSERT_EQ, #ASSERT_TRUE, #EVTCNT, #EVTSENT, #REPORT +** +******************************************************************************/ +#define TEARDOWN(FN) (TestStat == CFE_PASS ? UT_TEARDOWN_impl(__FILE__, __LINE__, __func__, (#FN), (FN)) : false) + +/*****************************************************************************/ +/** +** \brief Marks the end of a block of tests. +** +** \par Description +** Implementation function and associated ENDBLOCK() macro. Most CFE API functions will be tested with +** a block of tests, each of which will be performed in sequence. This function does +** +** \par Assumptions, External Events, and Notes: +** None +** +** \sa #STARTBLOCK +** +******************************************************************************/ +void UT_ENDBLOCK_impl(const char *FileName, int LineNum, const char *TestName); +#define ENDBLOCK() UT_ENDBLOCK_impl(__FILE__, __LINE__, __func__) + #endif /* __UT_STUBS_H_ */