diff --git a/cmake/mission_defaults.cmake b/cmake/mission_defaults.cmake index e023e791a..dea048195 100644 --- a/cmake/mission_defaults.cmake +++ b/cmake/mission_defaults.cmake @@ -59,6 +59,6 @@ set(psp_SEARCH_PATH ".") # If ENABLE_UNIT_TEST is enabled, then include the cfe_assert library in # all targets. This can still be overridden in targets.cmake. if (ENABLE_UNIT_TESTS) - list(APPEND MISSION_GLOBAL_APPLIST cfe_assert cfe_testrunner cfe_testcase) + list(APPEND MISSION_GLOBAL_APPLIST cfe_assert cfe_testcase) endif (ENABLE_UNIT_TESTS) diff --git a/cmake/sample_defs/cpu1_cfe_es_startup.scr b/cmake/sample_defs/cpu1_cfe_es_startup.scr index 990942c52..e48880e15 100644 --- a/cmake/sample_defs/cpu1_cfe_es_startup.scr +++ b/cmake/sample_defs/cpu1_cfe_es_startup.scr @@ -1,8 +1,9 @@ -CFE_LIB, sample_lib, SAMPLE_LIB_Init, SAMPLE_LIB, 0, 0, 0x0, 0; -CFE_APP, sample_app, SAMPLE_APP_Main, SAMPLE_APP, 50, 16384, 0x0, 0; -CFE_APP, ci_lab, CI_Lab_AppMain, CI_LAB_APP, 60, 16384, 0x0, 0; -CFE_APP, to_lab, TO_Lab_AppMain, TO_LAB_APP, 70, 16384, 0x0, 0; -CFE_APP, sch_lab, SCH_Lab_AppMain, SCH_LAB_APP, 80, 16384, 0x0, 0; +CFE_LIB, cfe_assert, CFE_Assert_LibInit, ASSERT_LIB, 0, 0, 0x0, 0; +CFE_LIB, sample_lib, SAMPLE_LIB_Init, SAMPLE_LIB, 0, 0, 0x0, 0; +CFE_APP, sample_app, SAMPLE_APP_Main, SAMPLE_APP, 50, 16384, 0x0, 0; +CFE_APP, ci_lab, CI_Lab_AppMain, CI_LAB_APP, 60, 16384, 0x0, 0; +CFE_APP, to_lab, TO_Lab_AppMain, TO_LAB_APP, 70, 16384, 0x0, 0; +CFE_APP, sch_lab, SCH_Lab_AppMain, SCH_LAB_APP, 80, 16384, 0x0, 0; ! ! Startup script fields: ! 1. Object Type -- CFE_APP for an Application, or CFE_LIB for a library. diff --git a/modules/cfe_assert/CMakeLists.txt b/modules/cfe_assert/CMakeLists.txt index d4cc58182..10ae4428d 100644 --- a/modules/cfe_assert/CMakeLists.txt +++ b/modules/cfe_assert/CMakeLists.txt @@ -4,6 +4,7 @@ project(CFE_ASSERT C) add_cfe_app(cfe_assert src/cfe_assert_io.c src/cfe_assert_init.c + src/cfe_assert_runner.c $ ) diff --git a/modules/cfe_assert/inc/cfe_assert.h b/modules/cfe_assert/inc/cfe_assert.h index 9ca074134..f85b6338a 100644 --- a/modules/cfe_assert/inc/cfe_assert.h +++ b/modules/cfe_assert/inc/cfe_assert.h @@ -38,6 +38,7 @@ ** Includes *************************************************************************/ #include "common_types.h" +#include "cfe_es_api_typedefs.h" /************************************************************************ ** Type Definitions @@ -50,11 +51,11 @@ typedef void (*CFE_Assert_StatusCallback_t)(uint8 MessageType, const char *Prefi *************************************************************************/ /************************************************************************/ -/** \brief Application Entry Point Function +/** \brief Test Application Entry Point Function ** ** \par Description -** This function should be specified in the cfe_es_startup.scr file -** as part of starting this application. +** This function should be specified as the entry point for an application +** that implements functional tests. ** ** \par Assumptions, External Events, and Notes: ** None @@ -64,6 +65,47 @@ typedef void (*CFE_Assert_StatusCallback_t)(uint8 MessageType, const char *Prefi *************************************************************************/ void CFE_Assert_AppMain(void); +/************************************************************************/ +/** \brief Library Init Function +** +** \par Description +** This function should be specified in the cfe_es_startup.scr file +** as part of loading this library. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS if successful, or error code +** +*************************************************************************/ +int32 CFE_Assert_LibInit(CFE_ES_LibId_t LibId); + +/************************************************************************/ +/** \brief Start Test +** +** \par Description +** +** \par Assumptions, External Events, and Notes: +** Must be followed by a call to CFE_Assert_ExecuteTest() +** +** \return None +** +*************************************************************************/ +int32 CFE_Assert_RegisterTest(const char *TestName); + +/************************************************************************/ +/** \brief Execute Test and Exit +** +** \par Description +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return None +** +*************************************************************************/ +void CFE_Assert_ExecuteTest(void); + /************************************************************************/ /** \brief Register a test status callback * diff --git a/modules/cfe_assert/src/cfe_assert_init.c b/modules/cfe_assert/src/cfe_assert_init.c index 57d0ac9b5..d2852d92d 100644 --- a/modules/cfe_assert/src/cfe_assert_init.c +++ b/modules/cfe_assert/src/cfe_assert_init.c @@ -47,11 +47,22 @@ void CFE_Assert_RegisterCallback(CFE_Assert_StatusCallback_t Callback) /* * Initialization Function for this library */ -int32 CFE_Assert_LibInit(uint32 LibId) +int32 CFE_Assert_LibInit(CFE_ES_LibId_t LibId) { + int32 status; + + memset(&CFE_Assert_Global, 0, sizeof(CFE_Assert_Global)); + UtTest_EarlyInit(); UT_BSP_Setup(); + status = OS_MutSemCreate(&CFE_Assert_Global.AccessMutex, "CFE_Assert", 0); + if (status != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("%s(): OS_MutSemCreate failed, rc=%d\n", __func__, (int)status); + return CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + /* * Start a test case for all startup logic. * diff --git a/modules/cfe_assert/src/cfe_assert_io.c b/modules/cfe_assert/src/cfe_assert_io.c index b68326e81..a9561222b 100644 --- a/modules/cfe_assert/src/cfe_assert_io.c +++ b/modules/cfe_assert/src/cfe_assert_io.c @@ -140,6 +140,4 @@ void UT_BSP_EndTest(const UtAssert_TestCounter_t *TestCounters) CFE_ES_WriteToSysLog("TEST COMPLETE: %u tests Segment(s) executed\n\n", (unsigned int)TestCounters->TestSegmentCount); - - OS_TaskExit(); } diff --git a/modules/cfe_assert/src/cfe_assert_priv.h b/modules/cfe_assert/src/cfe_assert_priv.h index 6bfb88b9d..75243bb95 100644 --- a/modules/cfe_assert/src/cfe_assert_priv.h +++ b/modules/cfe_assert/src/cfe_assert_priv.h @@ -39,6 +39,21 @@ *************************************************************************/ #include "common_types.h" #include "cfe_assert.h" +#include "cfe_mission_cfg.h" + +/** + * State of the CFE assert library. + * + * Note that typically tests need to be deferred until the CFE system + * reaches "operational" state. CFE assert has its own internal state + * that needs to be managed as well. + */ +typedef enum +{ + CFE_Assert_State_INIT, /**< Initial state prior to CFE_Assert_LibInit() */ + CFE_Assert_State_STARTUP, /**< cFE starting: successful CFE_Assert_LibInit(), but no tests run yet. */ + CFE_Assert_State_ACTIVE /**< cFE operational: Normal test applications are allowed to run */ +} CFE_Assert_State_Enum_t; /************************************************************************ ** Type Definitions @@ -46,12 +61,34 @@ typedef struct { + CFE_Assert_State_Enum_t LibState; + + /** + * Verbosity of default (syslog) output + * + * This controls the type(s) of assert messages that will be written to syslog. + * This only applies for default syslog output. Use of a status callback + * function overrides this. + */ uint32 CurrVerbosity; /** * Function to invoke to report test status */ CFE_Assert_StatusCallback_t StatusCallback; + + /** + * Mutex to control access to UtAssert structures. + * + * The UtAssert library is designed for single-threaded testing. To use it + * in a multi-threaded environment like CFE, it requires synchronization between + * apps, such that only one test app registers/runs tests at a time. + */ + osal_id_t AccessMutex; + + char CurrentTestName[CFE_MISSION_MAX_API_LEN]; + CFE_ES_AppId_t OwnerAppId; + } CFE_Assert_Global_t; extern CFE_Assert_Global_t CFE_Assert_Global; diff --git a/modules/cfe_testrunner/src/cfe_testrunner_main.c b/modules/cfe_assert/src/cfe_assert_runner.c similarity index 53% rename from modules/cfe_testrunner/src/cfe_testrunner_main.c rename to modules/cfe_assert/src/cfe_assert_runner.c index e2e8b0cbe..d9953c498 100644 --- a/modules/cfe_testrunner/src/cfe_testrunner_main.c +++ b/modules/cfe_assert/src/cfe_assert_runner.c @@ -31,8 +31,7 @@ #include "cfe.h" -#include "cfe_testrunner.h" -#include "cfe_assert.h" +#include "cfe_assert_priv.h" #include "uttest.h" #include "utbsp.h" @@ -55,7 +54,7 @@ * * The value is in milliseconds. */ -#define CFE_TESTRUNNER_START_DELAY 4000 +#define CFE_TESTRUNNER_START_DELAY 1000 /* * This uses the message type as the event ID, because these are already @@ -70,7 +69,7 @@ static CFE_EVS_BinFilter_t CFE_TR_EventFilters[] = { {UTASSERT_CASETYPE_DEBUG, CFE_EVS_NO_FILTER}, }; -void CFE_TR_StatusReport(uint8 MessageType, const char *Prefix, const char *OutputMessage) +void CFE_Assert_StatusReport(uint8 MessageType, const char *Prefix, const char *OutputMessage) { uint16 EventType; @@ -101,68 +100,123 @@ void CFE_TR_StatusReport(uint8 MessageType, const char *Prefix, const char *Outp CFE_EVS_SendEvent(MessageType, EventType, "[%5s] %s", Prefix, OutputMessage); } -/* - * Entry point for this application - */ -void CFE_TR_AppMain(void) +int32 CFE_Assert_RegisterTest(const char *TestName) { - int32 rc; - uint32 RunStatus; + int32 rc; + char SetupSegmentName[64]; rc = CFE_EVS_Register(CFE_TR_EventFilters, sizeof(CFE_TR_EventFilters) / sizeof(CFE_EVS_BinFilter_t), CFE_EVS_EventFilter_BINARY); if (rc != CFE_SUCCESS) { CFE_ES_WriteToSysLog("Error from CFE_EVS_Register: %08lx\n", (unsigned long)rc); - return; + return rc; } /* * Delay until the system reaches "operational" state -- this is when all libs have initialized * and all apps have reached their RunLoop. + * + * If already operational then this should return immediately. This may be the case if/when + * test apps are started via ES command. */ rc = CFE_ES_WaitForSystemState(CFE_ES_SystemState_OPERATIONAL, CFE_TESTRUNNER_MAX_STARTUP_WAIT); if (rc != CFE_SUCCESS) { CFE_ES_WriteToSysLog("Error while waiting for OPERATIONAL state: %08lx\n", (unsigned long)rc); - return; + return rc; } /* - * Startup Phase has ended. + * Acquire the mutex. This is needed because UtAssert and its data structures are not thread-safe. + * Only one test app should use UtAssert facilities at a given time. */ - UtAssert_EndTest(); + rc = OS_MutSemTake(CFE_Assert_Global.AccessMutex); + if (rc != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("%s(): Error from OS_MutSemTake(): %d\n", __func__, (int)rc); + return CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } /* - * Use the local status report function for the remainder of tests + * After acquiring mutex, record the fact that this app now "owns" the assert functions */ - CFE_Assert_RegisterCallback(CFE_TR_StatusReport); + rc = CFE_ES_GetAppID(&CFE_Assert_Global.OwnerAppId); + if (rc != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("%s(): Error from CFE_ES_GetAppId(): %08x\n", __func__, (unsigned int)rc); + OS_MutSemGive(CFE_Assert_Global.AccessMutex); + return rc; + } /* - * Note - in a normal app this would be a while loop, - * but is just an "if" right now as it only runs once. - * - * A future enhancement to this app might be to create an SB pipe - * and execute tests based on the receipt of a command to do so. - * - * For now, it just works in a one-shot mode to run tests that were - * registered during startup, then it self-exits. + * This means the system is operational and at least one app needs to run tests. + * Update library state accordingly. The first test app that gets to this point + * will handle this. */ - RunStatus = CFE_ES_RunStatus_APP_RUN; - if (CFE_ES_RunLoop(&RunStatus)) + if (CFE_Assert_Global.LibState != CFE_Assert_State_ACTIVE) { + UtAssert_EndTest(); + CFE_Assert_Global.LibState = CFE_Assert_State_ACTIVE; + OS_TaskDelay(CFE_TESTRUNNER_START_DELAY); + } + + /* + * This resets the underlying UtAssert test list to a clean slate + * + * NOTE: this is not the ideal API here; UtAssert was originally designed to + * run one set of tests and then exit. In this environment it might be used + * repeatedly by different apps. The "EarlyInit" will wipe and reset the + * internal global, which works, but is heavy handed and doesn't account for + * the possibility that tests have been added without running. + */ + UtTest_EarlyInit(); + + strncpy(CFE_Assert_Global.CurrentTestName, TestName, sizeof(CFE_Assert_Global.CurrentTestName) - 1); + CFE_Assert_Global.CurrentTestName[sizeof(CFE_Assert_Global.CurrentTestName) - 1] = 0; + + /* Use the local status report function for the remainder of tests */ + CFE_Assert_RegisterCallback(CFE_Assert_StatusReport); + + /* Start a test group in case UtAssert is used during setup phase */ + snprintf(SetupSegmentName, sizeof(SetupSegmentName), "%s TEST SETUP", TestName); + UtAssert_BeginTest(SetupSegmentName); + + return CFE_SUCCESS; +} - /* - * Run all registered test cases. - */ - UtTest_Run(); +void CFE_Assert_ExecuteTest(void) +{ + int32 rc; + CFE_ES_AppId_t AppId; - /* - * Exit the main task. - */ - RunStatus = CFE_ES_RunStatus_APP_EXIT; + /* + * Sanity check - This should only be called from the same app after CFE_Assert_RegisterTest() + */ + rc = CFE_ES_GetAppID(&AppId); + if (rc != CFE_SUCCESS || !CFE_RESOURCEID_TEST_EQUAL(AppId, CFE_Assert_Global.OwnerAppId)) + { + CFE_ES_WriteToSysLog("%s(): Invalid calling context, CFE_ES_GetAppId() rc=%08x AppId=%lx, OwnerAppId=%lx\n", + __func__, (unsigned int)rc, CFE_RESOURCEID_TO_ULONG(AppId), + CFE_RESOURCEID_TO_ULONG(CFE_Assert_Global.OwnerAppId)); + return; } - CFE_ES_ExitApp(RunStatus); + UtAssert_EndTest(); + + OS_TaskDelay(CFE_TESTRUNNER_START_DELAY); + + /* Run all registered test cases. */ + UtTest_Run(); + + /* unregister the callback and unset the appid */ + CFE_Assert_RegisterCallback(NULL); + + /* Release the access mutex so the next test program can run */ + rc = OS_MutSemGive(CFE_Assert_Global.AccessMutex); + if (rc != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("%s(): Error from OS_MutSemGive(): %d\n", __func__, (int)rc); + } } diff --git a/modules/cfe_testcase/CMakeLists.txt b/modules/cfe_testcase/CMakeLists.txt index 4bf6c0058..64f4180cf 100644 --- a/modules/cfe_testcase/CMakeLists.txt +++ b/modules/cfe_testcase/CMakeLists.txt @@ -1,9 +1,9 @@ -include_directories("${CFE_ASSERT_SOURCE_DIR}/inc") -include_directories("${UT_ASSERT_SOURCE_DIR}/inc") - -# Filenames based on doxygen groups. +# Filenames based on doxygen groups. # Create the app module add_cfe_app(cfe_testcase src/cfe_test.c src/es_info_test.c ) + +# register the dependency on cfe_assert +add_cfe_app_dependency(cfe_testcase cfe_assert) diff --git a/modules/cfe_testcase/src/cfe_test.c b/modules/cfe_testcase/src/cfe_test.c index 2b25755a6..f4015320c 100644 --- a/modules/cfe_testcase/src/cfe_test.c +++ b/modules/cfe_testcase/src/cfe_test.c @@ -30,14 +30,35 @@ * Includes */ +#include "cfe_assert.h" #include "cfe_test.h" /* - * Initialization function + * Test main function * Register this test routine with CFE Assert */ -int32 CFE_Test_Init(int32 LibId) +void CFE_TestMain(void) { - ESInfoTestSetup(LibId); - return CFE_SUCCESS; + /* + * Register this test app with CFE assert + * + * Note this also waits for the appropriate overall system + * state and gets ownership of the UtAssert subsystem + */ + CFE_Assert_RegisterTest("CFE API"); + + /* + * Register test cases in UtAssert + */ + ESInfoTestSetup(); + + /* + * Execute the tests + * + * Note this also releases ownership of the UtAssert subsystem when complete + */ + CFE_Assert_ExecuteTest(); + + /* Nothing more for this app to do */ + CFE_ES_ExitApp(CFE_ES_RunStatus_APP_EXIT); } diff --git a/modules/cfe_testcase/src/cfe_test.h b/modules/cfe_testcase/src/cfe_test.h index dced3d00c..5d0fe162b 100644 --- a/modules/cfe_testcase/src/cfe_test.h +++ b/modules/cfe_testcase/src/cfe_test.h @@ -52,7 +52,7 @@ #define UtAssert_ResourceID_Undifeined(id) \ UtAssert_True(!CFE_RESOURCEID_TEST_DEFINED(id), "%s (%lu) not defined", #id, CFE_RESOURCEID_TO_ULONG(id)) -int32 CFE_Test_Init(int32 LibId); -int32 ESInfoTestSetup(int32 LibId); +void CFE_TestMain(void); +int32 ESInfoTestSetup(void); #endif /* CFE_TEST_H */ diff --git a/modules/cfe_testcase/src/es_info_test.c b/modules/cfe_testcase/src/es_info_test.c index f389394e4..5885e8968 100644 --- a/modules/cfe_testcase/src/es_info_test.c +++ b/modules/cfe_testcase/src/es_info_test.c @@ -38,7 +38,7 @@ void TestAppInfo(void) CFE_ES_AppId_t TestAppId; CFE_ES_AppId_t ESAppId; CFE_ES_AppId_t AppIdByName; - const char * TestAppName = "TESTRUN_APP"; + const char * TestAppName = "CFE_TEST_APP"; const char * ESAppName = "CFE_ES"; const char * InvalidName = "INVALID_NAME"; char AppNameBuf[OS_MAX_API_NAME + 4]; @@ -257,7 +257,7 @@ void TestModuleInfo(void) UtAssert_INT32_EQ(CFE_ES_GetModuleInfo(NULL, CFE_RESOURCEID_UNWRAP(TestAppId)), CFE_ES_BAD_ARGUMENT); } -int32 ESInfoTestSetup(int32 LibId) +int32 ESInfoTestSetup(void) { UtTest_Add(TestAppInfo, NULL, NULL, "Test App Info"); UtTest_Add(TestTaskInfo, NULL, NULL, "Test Task Info"); diff --git a/modules/cfe_testrunner/CMakeLists.txt b/modules/cfe_testrunner/CMakeLists.txt deleted file mode 100644 index b4df50bc6..000000000 --- a/modules/cfe_testrunner/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -project(CFE_TESTRUNNER C) - -# Create the app module -add_cfe_app(cfe_testrunner - src/cfe_testrunner_main.c -) - -target_include_directories(cfe_testrunner PUBLIC - ${CFE_TESTRUNNER_SOURCE_DIR}/inc -) - -# register the dependency on cfe_assert -add_cfe_app_dependency(cfe_testrunner cfe_assert) diff --git a/modules/cfe_testrunner/inc/cfe_testrunner.h b/modules/cfe_testrunner/inc/cfe_testrunner.h deleted file mode 100644 index d4ae6cb96..000000000 --- a/modules/cfe_testrunner/inc/cfe_testrunner.h +++ /dev/null @@ -1,65 +0,0 @@ -/************************************************************************* -** -** GSC-18128-1, "Core Flight Executive Version 6.7" -** -** Copyright (c) 2006-2019 United States Government as represented by -** the Administrator of the National Aeronautics and Space Administration. -** All Rights Reserved. -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -** -** File: cfe_testrunner.h -** -** Purpose: -** Specification for the CFE testrunner (UT testrunner wrapper) functions. -** -*************************************************************************/ - -/** - * @file - * - * Declarations and prototypes for cfe_testrunner module - */ - -#ifndef CFE_TESTRUNNER_H -#define CFE_TESTRUNNER_H - -/************************************************************************ -** Includes -*************************************************************************/ -#include "common_types.h" - -/************************************************************************ -** Type Definitions -*************************************************************************/ - -/************************************************************************* -** Exported Functions -*************************************************************************/ - -/************************************************************************/ -/** \brief Application Entry Point Function -** -** \par Description -** This function should be specified in the cfe_es_startup.scr file -** as part of starting this application. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \return Execution status, see \ref CFEReturnCodes -** -*************************************************************************/ -void CFE_TR_AppMain(void); - -#endif /* CFE_TESTRUNNER_H */