Skip to content

Commit

Permalink
Fix #1281, functional test as app instead of library
Browse files Browse the repository at this point in the history
Merge the "testrunner" app and put the runner logic into cfe_assert lib.
Converts the "testcase" library into an app that simply calls the logic
in cfe_assert to do the test.

This means that the test can be run on demand by issuing the ES
start app command, rather than putting it into the startup script.
  • Loading branch information
jphickey committed Apr 12, 2021
1 parent e80aae9 commit 99be4cd
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 136 deletions.
2 changes: 1 addition & 1 deletion cmake/mission_defaults.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)

11 changes: 6 additions & 5 deletions cmake/sample_defs/cpu1_cfe_es_startup.scr
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 1 addition & 0 deletions modules/cfe_assert/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
$<TARGET_OBJECTS:ut_assert_pic>
)

Expand Down
48 changes: 45 additions & 3 deletions modules/cfe_assert/inc/cfe_assert.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
** Includes
*************************************************************************/
#include "common_types.h"
#include "cfe_es_api_typedefs.h"

/************************************************************************
** Type Definitions
Expand All @@ -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
Expand All @@ -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
*
Expand Down
13 changes: 12 additions & 1 deletion modules/cfe_assert/src/cfe_assert_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
2 changes: 0 additions & 2 deletions modules/cfe_assert/src/cfe_assert_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
37 changes: 37 additions & 0 deletions modules/cfe_assert/src/cfe_assert_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,56 @@
*************************************************************************/
#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
*************************************************************************/

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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;
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();

/*
* Run all registered test cases.
*/
UtTest_Run();
strncpy(CFE_Assert_Global.CurrentTestName, TestName, sizeof(CFE_Assert_Global.CurrentTestName)-1);
CFE_Assert_Global.CurrentTestName[sizeof(CFE_Assert_Global.CurrentTestName)-1] = 0;

/*
* Exit the main task.
*/
RunStatus = CFE_ES_RunStatus_APP_EXIT;
/* 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;
}

void CFE_Assert_ExecuteTest(void)
{
int32 rc;
CFE_ES_AppId_t AppId;

/*
* 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);
}
}

8 changes: 4 additions & 4 deletions modules/cfe_testcase/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit 99be4cd

Please sign in to comment.