From 1741655fad855cd68d4a83c08e2bd0fade18fad6 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Sat, 13 Jun 2020 12:33:06 -0400 Subject: [PATCH] Fix #501, add PIC library for ut assert Add a position independent code (PIC) variant of the ut_assert library, which can be dynamically loaded into other applications rather than running as a standalone OSAL application. This enables loading UT assert as a CFE library. This required moving some symbols/functions around. Mainly, the PIC library does _not_ contain the "utbsp.c" file which has the bindings to the OSAL BSP when running as a standalone app, but everything else should be the same. --- ut_assert/CMakeLists.txt | 21 ++++++++++ ut_assert/inc/utassert.h | 32 +++++++++++++++ ut_assert/inc/utbsp.h | 32 --------------- ut_assert/inc/uttest.h | 35 +++++++++++++++-- ut_assert/src/utassert.c | 47 +++++++++++++++++++++- ut_assert/src/utbsp.c | 84 ++++++++++++++++++++-------------------- ut_assert/src/utglobal.h | 59 ++++++++++++++++++++++++++++ ut_assert/src/uttest.c | 63 +++++++----------------------- 8 files changed, 243 insertions(+), 130 deletions(-) create mode 100644 ut_assert/src/utglobal.h diff --git a/ut_assert/CMakeLists.txt b/ut_assert/CMakeLists.txt index 58b1aefaa..5570be30a 100644 --- a/ut_assert/CMakeLists.txt +++ b/ut_assert/CMakeLists.txt @@ -42,4 +42,25 @@ target_compile_definitions(ut_assert PUBLIC target_link_libraries(ut_assert osal_bsp) +# The "pic" variant of ut_assert is compiled as an +# object library to be included in another object, +# such as a loadable test app for CFE. +# It is compiled as position independent code (PIC) +# to support dynamic loading. +add_library(ut_assert_pic OBJECT EXCLUDE_FROM_ALL + src/utassert.c + src/utlist.c + src/utstubs.c + src/uttest.c + src/uttools.c +) + +set_target_properties(ut_assert_pic PROPERTIES + POSITION_INDEPENDENT_CODE TRUE +) + +target_include_directories(ut_assert_pic PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/inc" +) + diff --git a/ut_assert/inc/utassert.h b/ut_assert/inc/utassert.h index 9d277137a..9cdbb426b 100644 --- a/ut_assert/inc/utassert.h +++ b/ut_assert/inc/utassert.h @@ -188,5 +188,37 @@ void UtAssert_Abort(const char *Message); */ void UtAssert_Message(uint8 MessageType, const char *File, uint32 Line, const char *Spec, ...) OS_PRINTF(4,5); +/** + * The BSP single test case reporting function. + * + * Invokes the BSP-specific pass/fail reporting mechanism based on the MessageType. + * + * This is typically output as a message to the test log but may be fancier if the BSP requires it. + * One example might be to toggle a GPIO bit or LED if the test is running on a separate processor board. + * + * \param File File containing the test case + * \param LineNum Line number containing the test case + * \param MessageType Should be set to either UT_MESSAGE_PASS or UT_MESSAGE_FAILURE. + * \param SubsysName The subsystem under test (abbreviated name) + * \param ShortDesc Short description of the test case + * \param SegmentNum Sequence among the overall/global test Segments + * \param TestDescr Sequence within the current test Segment + */ +void UtAssert_DoReport(const char *File, uint32 LineNum, uint32 SegmentNum, uint32 SegmentSeq, uint8 MessageType, + const char *SubsysName, const char *ShortDesc); + +/** + * The BSP overall test reporting function. + * + * Invokes the BSP-specific overall pass/fail reporting mechanism based the subsystem pass/fail counters. + * + * Like the UtAssert_DoReport() function, this is typically done as a message on the console/log however + * it might be different for embedded targets. + * + * \param Appname The application under test + * \param TestCounters Counter object for the completed test + */ +void UtAssert_DoTestSegmentReport(const char *SegmentName, const UtAssert_TestCounter_t *TestCounters); + #endif diff --git a/ut_assert/inc/utbsp.h b/ut_assert/inc/utbsp.h index c52291fc6..2bbab0df7 100644 --- a/ut_assert/inc/utbsp.h +++ b/ut_assert/inc/utbsp.h @@ -79,38 +79,6 @@ void UT_BSP_StartTestSegment(uint32 SegmentNumber, const char *SegmentName); */ void UT_BSP_DoText(uint8 MessageType, const char *OutputMessage); -/** - * The BSP single test case reporting function. - * - * Invokes the BSP-specific pass/fail reporting mechanism based on the MessageType. - * - * This is typically output as a message to the test log but may be fancier if the BSP requires it. - * One example might be to toggle a GPIO bit or LED if the test is running on a separate processor board. - * - * \param File File containing the test case - * \param LineNum Line number containing the test case - * \param MessageType Should be set to either UT_MESSAGE_PASS or UT_MESSAGE_FAILURE. - * \param SubsysName The subsystem under test (abbreviated name) - * \param ShortDesc Short description of the test case - * \param SegmentNum Sequence among the overall/global test Segments - * \param TestDescr Sequence within the current test Segment - */ -void UT_BSP_DoReport(const char *File, uint32 LineNum, uint32 SegmentNum, uint32 SegmentSeq, uint8 MessageType, - const char *SubsysName, const char *ShortDesc); - -/** - * The BSP overall test reporting function. - * - * Invokes the BSP-specific overall pass/fail reporting mechanism based the subsystem pass/fail counters. - * - * Like the UT_BSP_DoReport() function, this is typically done as a message on the console/log however - * it might be different for embedded targets. - * - * \param Appname The application under test - * \param TestCounters Counter object for the completed test - */ -void UT_BSP_DoTestSegmentReport(const char *SegmentName, const UtAssert_TestCounter_t *TestCounters); - /** * The BSP overall test end function. * diff --git a/ut_assert/inc/uttest.h b/ut_assert/inc/uttest.h index ac881eaa1..66e63d21e 100644 --- a/ut_assert/inc/uttest.h +++ b/ut_assert/inc/uttest.h @@ -29,14 +29,41 @@ * Exported Functions */ -/* Adds a new unit test to the test database. */ +/** + * \brief Adds a new unit test to the test database. + * + * Called by the user to register a new test case with the library. + * + * \param Test Main test function to call. + * \param Setup Setup function, called before the test function + * \param Teardown Cleanup function, called after the test function + * \param TestName Name of test for logging purposes + */ void UtTest_Add(void (*Test)(void), void (*Setup)(void), void (*Teardown)(void), const char *TestName); +/** + * \brief Early initialization function + * + * Reset the global data to a safe state for initial start-up. + * This should be called before any other API. + */ +void UtTest_EarlyInit(void); + + +/** + * \brief Execute all registered tests + * + * All test functions that were registered with UtTest_Add will be executed in order. + */ +void UtTest_Run(void); + /* - * Set up function for UT-Assert based test routines - * This should call UtTest_Add() for each test set + * \brief Set up function for UT-Assert based test routines + * + * This function must be provided by the user to set up test cases. + * This should call UtTest_Add() for each test case. */ -void UtTest_Setup(void); +void UtTest_Setup(void); #endif diff --git a/ut_assert/src/utassert.c b/ut_assert/src/utassert.c index 0ce18852b..a93e9d972 100644 --- a/ut_assert/src/utassert.c +++ b/ut_assert/src/utassert.c @@ -39,6 +39,49 @@ static char CurrentSegment[128]; * Function Definitions */ +void UtAssert_DoReport(const char *File, uint32 LineNum, uint32 SegmentNum, uint32 TestSeq, uint8 MessageType, + const char *SubsysName, const char *ShortDesc) +{ + uint32 FileLen; + const char *BasePtr; + char ReportBuffer[320]; + + FileLen = strlen(File); + BasePtr = File + FileLen; + while (FileLen > 0) + { + --BasePtr; + --FileLen; + if (*BasePtr == '/' || *BasePtr == '\\') + { + ++BasePtr; + break; + } + } + + snprintf(ReportBuffer, sizeof(ReportBuffer), "%02u.%03u %s:%u - %s", (unsigned int)SegmentNum, + (unsigned int)TestSeq, BasePtr, (unsigned int)LineNum, ShortDesc); + + UT_BSP_DoText(MessageType, ReportBuffer); +} + +void UtAssert_DoTestSegmentReport(const char *SegmentName, const UtAssert_TestCounter_t *TestCounters) +{ + char ReportBuffer[128]; + + snprintf(ReportBuffer, sizeof(ReportBuffer), + "%02u %-20s TOTAL::%-4u PASS::%-4u FAIL::%-4u MIR::%-4u TSF::%-4u N/A::%-4u\n", + (unsigned int)TestCounters->TestSegmentCount, SegmentName, (unsigned int)TestCounters->TotalTestCases, + (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_PASS], + (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_FAILURE], + (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_MIR], + (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_TSF], + (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_NA]); + + UT_BSP_DoText(UTASSERT_CASETYPE_END, ReportBuffer); +} + + uint32 UtAssert_GetPassCount(void) { return(UT_TotalCounters.CaseCount[UTASSERT_CASETYPE_PASS]); @@ -80,7 +123,7 @@ void UtAssert_EndTest(void) { UT_TotalCounters.CaseCount[Ct] += UT_SegmentCounters.CaseCount[Ct]; } - UT_BSP_DoTestSegmentReport(CurrentSegment, &UT_SegmentCounters); + UtAssert_DoTestSegmentReport(CurrentSegment, &UT_SegmentCounters); } else { @@ -126,7 +169,7 @@ bool UtAssertEx(bool Expression, UtAssert_CaseType_t CaseType, const char *File, vsnprintf(FinalMessage, sizeof(FinalMessage), MessageFormat, va); va_end(va); - UT_BSP_DoReport(File, Line, 1 + UT_TotalCounters.TestSegmentCount, UT_SegmentCounters.TotalTestCases, CaseType, CurrentSegment, FinalMessage); + UtAssert_DoReport(File, Line, 1 + UT_TotalCounters.TestSegmentCount, UT_SegmentCounters.TotalTestCases, CaseType, CurrentSegment, FinalMessage); return Expression; } diff --git a/ut_assert/src/utbsp.c b/ut_assert/src/utbsp.c index f63e57923..524f8c8bc 100644 --- a/ut_assert/src/utbsp.c +++ b/ut_assert/src/utbsp.c @@ -13,6 +13,11 @@ ** Purpose: ** Unit test BSP interface functions. ** +** This file provides the bindings between the OSAL BSP and UT assert +** when directly running a test program as a standalone OSAL application. +** +** It is not used when loading UT assert into another application (e.g. CFE). +** ******************************************************************************/ #include @@ -178,48 +183,6 @@ void UT_BSP_DoText(uint8 MessageType, const char *OutputMessage) } } -void UT_BSP_DoReport(const char *File, uint32 LineNum, uint32 SegmentNum, uint32 TestSeq, uint8 MessageType, - const char *SubsysName, const char *ShortDesc) -{ - uint32 FileLen; - const char *BasePtr; - char ReportBuffer[128]; - - FileLen = strlen(File); - BasePtr = File + FileLen; - while (FileLen > 0) - { - --BasePtr; - --FileLen; - if (*BasePtr == '/' || *BasePtr == '\\') - { - ++BasePtr; - break; - } - } - - snprintf(ReportBuffer, sizeof(ReportBuffer), "%02u.%03u %s:%u - %s", (unsigned int)SegmentNum, - (unsigned int)TestSeq, BasePtr, (unsigned int)LineNum, ShortDesc); - - UT_BSP_DoText(MessageType, ReportBuffer); -} - -void UT_BSP_DoTestSegmentReport(const char *SegmentName, const UtAssert_TestCounter_t *TestCounters) -{ - char ReportBuffer[128]; - - snprintf(ReportBuffer, sizeof(ReportBuffer), - "%02u %-20s TOTAL::%-4u PASS::%-4u FAIL::%-4u MIR::%-4u TSF::%-4u N/A::%-4u\n", - (unsigned int)TestCounters->TestSegmentCount, SegmentName, (unsigned int)TestCounters->TotalTestCases, - (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_PASS], - (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_FAILURE], - (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_MIR], - (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_TSF], - (unsigned int)TestCounters->CaseCount[UTASSERT_CASETYPE_NA]); - - UT_BSP_DoText(UTASSERT_CASETYPE_END, ReportBuffer); -} - void UT_BSP_EndTest(const UtAssert_TestCounter_t *TestCounters) { char Message[128]; @@ -230,7 +193,7 @@ void UT_BSP_EndTest(const UtAssert_TestCounter_t *TestCounters) */ if (TestCounters->TestSegmentCount > 1) { - UT_BSP_DoTestSegmentReport("SUMMARY", TestCounters); + UtAssert_DoTestSegmentReport("SUMMARY", TestCounters); } snprintf(Message, sizeof(Message), "COMPLETE: %u tests Segment(s) executed\n\n", @@ -246,3 +209,38 @@ void UT_BSP_EndTest(const UtAssert_TestCounter_t *TestCounters) OS_BSP_SetExitCode(OS_SUCCESS); } } + +/* + * ------------------------------------------------------- + * ENTRY POINTS from OSAL BSP + * ------------------------------------------------------- + */ + +void OS_Application_Run(void) +{ + UtTest_Run(); +} + +/* + * Entry point from the BSP. + * When linking with UT-Assert, the test framework (this library) serves + * the role of the "application" being executed. + * + * There is a separate entry point (UT_Test_Setup) to configure the test cases. + */ +void OS_Application_Startup(void) +{ + + UT_BSP_Setup(); + + /* + * Wrap the UtTest_Setup() function in a UT segment called "SETUP" + * This allows any assert calls to be used and recorded during setup + */ + UtAssert_BeginTest("SETUP"); + UtTest_Setup(); + UtAssert_EndTest(); +} + + + diff --git a/ut_assert/src/utglobal.h b/ut_assert/src/utglobal.h new file mode 100644 index 000000000..52f6ae86d --- /dev/null +++ b/ut_assert/src/utglobal.h @@ -0,0 +1,59 @@ +/* +** +** File: uttest.c +** +** Copyright 2012-2013 United States Government as represented by the +** Administrator of the National Aeronautics and Space Administration. +** All Other Rights Reserved. +** +** This software was created at NASA's Goddard Space Flight Center. +** This software is governed by the NASA Open Source Agreement and may be +** used, distributed and modified only pursuant to the terms of that +** agreement. +** +** Purpose: This file contains functions to implement a standard way to execute unit tests. +** +*/ + +/* + * Includes + */ + +#ifndef INCLUDE_UTASSERT_GLOBAL_H_ +#define INCLUDE_UTASSERT_GLOBAL_H_ + +#include "osapi.h" +#include "utassert.h" +#include "utlist.h" +#include "utbsp.h" +#include "uttest.h" +#include "utstubs.h" + +/* + * Type Definitions + */ + +typedef struct +{ + void (*Test)(void); + void (*Setup)(void); + void (*Teardown)(void); + + /* Note - the name entry should be long enough to support a GroupName.TestName pattern, + * hence why it uses double the OS_MAX_API_NAME length */ + char TestName[OS_MAX_API_NAME*2]; +} UtTestDataBaseEntry_t; + +typedef struct +{ + UtListHead_t DataBase; + uint32 ExecutedCount; +} UtAssert_Global_t; + +/* + * Global Test Data + */ +extern UtAssert_Global_t UtAssert_Global; + +#endif /* INCLUDE_UTASSERT_GLOBAL_H_ */ + diff --git a/ut_assert/src/uttest.c b/ut_assert/src/uttest.c index f4a2bcb2f..3ce496424 100644 --- a/ut_assert/src/uttest.c +++ b/ut_assert/src/uttest.c @@ -19,30 +19,12 @@ * Includes */ -#include "osapi.h" -#include "utassert.h" -#include "utlist.h" -#include "utbsp.h" -#include "uttest.h" -#include "utstubs.h" +#include "utglobal.h" /* - * Type Definitions + * Global state instance */ - -typedef struct { - void (*Test)(void); - void (*Setup)(void); - void (*Teardown)(void); - const char *TestName; -} UtTestDataBaseEntry_t; - -/* - * Local Data - */ - -UtListHead_t UtTestDataBase; -uint32 UtTestsExecutedCount = 0; +UtAssert_Global_t UtAssert_Global; /* * Function Definitions @@ -52,23 +34,24 @@ void UtTest_Add(void (*Test)(void), void (*Setup)(void), void (*Teardown)(void), { UtTestDataBaseEntry_t UtTestDataBaseEntry; + memset(&UtTestDataBaseEntry, 0, sizeof(UtTestDataBaseEntry)); UtTestDataBaseEntry.Test = Test; UtTestDataBaseEntry.Setup = Setup; UtTestDataBaseEntry.Teardown = Teardown; - UtTestDataBaseEntry.TestName = TestName; - UtList_Add(&UtTestDataBase, &UtTestDataBaseEntry, sizeof(UtTestDataBaseEntry_t), 0); + strncpy(UtTestDataBaseEntry.TestName, TestName, sizeof(UtTestDataBaseEntry.TestName)-1); + UtList_Add(&UtAssert_Global.DataBase, &UtTestDataBaseEntry, sizeof(UtTestDataBaseEntry_t), 0); } -void OS_Application_Run(void) +void UtTest_Run(void) { uint32 i; UtListNode_t *UtListNode; UtTestDataBaseEntry_t *UtTestDataBaseEntry; - if (UtTestDataBase.NumberOfEntries > 0) { + if (UtAssert_Global.DataBase.NumberOfEntries > 0) { - UtListNode = UtTestDataBase.First; - for (i=0; i < UtTestDataBase.NumberOfEntries; i++) { + UtListNode = UtAssert_Global.DataBase.First; + for (i=0; i < UtAssert_Global.DataBase.NumberOfEntries; i++) { UtTestDataBaseEntry = UtListNode->Data; @@ -77,7 +60,7 @@ void OS_Application_Run(void) UtAssert_SetContext(UTASSERT_CASETYPE_TSF); if (UtTestDataBaseEntry->Setup) { UtTestDataBaseEntry->Setup(); } UtAssert_SetContext(UTASSERT_CASETYPE_FAILURE); - if (UtTestDataBaseEntry->Test) { UtTestDataBaseEntry->Test(); UtTestsExecutedCount++; } + if (UtTestDataBaseEntry->Test) { UtTestDataBaseEntry->Test(); UtAssert_Global.ExecutedCount++; } UtAssert_SetContext(UTASSERT_CASETYPE_TTF); if (UtTestDataBaseEntry->Teardown) { UtTestDataBaseEntry->Teardown(); } @@ -87,35 +70,17 @@ void OS_Application_Run(void) } } - UtList_Reset(&UtTestDataBase); + UtList_Reset(&UtAssert_Global.DataBase); UT_BSP_EndTest(UtAssert_GetCounters()); } -/* - * Entry point from the BSP. - * When linking with UT-Assert, the test framework (this library) serves - * the role of the "application" being executed. - * - * There is a separate entry point (UT_Test_Setup) to configure the test cases. - */ -void OS_Application_Startup(void) +void UtTest_EarlyInit(void) { /* * Reset the test global variables, just in case. */ - memset(&UtTestDataBase, 0, sizeof(UtTestDataBase)); - UtTestsExecutedCount = 0; - - UT_BSP_Setup(); - - /* - * Wrap the UtTest_Setup() function in a UT segment called "SETUP" - * This allows any assert calls to be used and recorded during setup - */ - UtAssert_BeginTest("SETUP"); - UtTest_Setup(); - UtAssert_EndTest(); + memset(&UtAssert_Global, 0, sizeof(UtAssert_Global)); }