diff --git a/config/default_cs_tbldefs.h b/config/default_cs_tbldefs.h index 36a0f19..362f432 100644 --- a/config/default_cs_tbldefs.h +++ b/config/default_cs_tbldefs.h @@ -113,6 +113,7 @@ typedef struct uint32 ComparisonValue; /**< \brief The Memory Integrity Value */ uint32 ByteOffset; /**< \brief Where a previous unfinished calc left off */ uint32 TempChecksumValue; /**< \brief The unfinished caluculation */ + CFE_ResourceId_t ResourceID; /**< \brief ES identity of the application or library */ char Name[CFE_MISSION_MAX_API_LEN]; /**< \brief name of the app */ } CS_Res_App_Table_Entry_t; diff --git a/docs/cs_FunctionalRequirements.csv b/docs/cs_FunctionalRequirements.csv index 4b6a9c7..ef71372 100644 --- a/docs/cs_FunctionalRequirements.csv +++ b/docs/cs_FunctionalRequirements.csv @@ -363,6 +363,12 @@ Table Checksum Miscomparisons,CS4051,"When the CS Application is comparing a cal ,,2) increment the relevant Miscompare Counter,,,,, ,,3) Issue an event message to notify the user of the miscomparison,,,,, Checksum State Order of Operations,CS4060,"When the CS Application is calculating checksums, the Checksum State of the table must be 'ENABLED' for an entry in the table to be checksummed, regardless of the state of ""Entry Checksum State""",,,"To ensure that individual entry checksum states are only acted upon when the overall checksumming for the table is enabled, maintaining a hierarchical control structure.",, +Application Reload Checksum Recovery,CS4070,"Before reading each checksum chunk for an Application Table entry naming an application or library, the CS Application shall resolve its Executive Services resource ID and valid code address and size, and shall:",,,"To recover from restarted or reloaded module instances without mixing their partial checksums or comparing against a prior instance baseline, while preserving corruption reporting for an unchanged instance.",nasa/CS#20; applies to background checksumming and commanded application baseline recomputation. Per-chunk lookup does not pin the module against concurrent unload., +,,1) Record the first successfully resolved identity without reporting a reload.,,,,, +,,"2) On a changed previously recorded resource ID, discard the partial checksum and offset, invalidate the previous baseline, record the new identity, and issue an informational module/baseline-reset event without changing enable states.",,,,, +,,3) Establish a baseline after the first complete pass over the new instance and compare subsequent completed checksums against it as specified in CS4050.,,,,, +,,"4) On code address or size change with the same resource ID, restart the partial computation from offset zero while retaining the baseline.",,,,, +,,"5) On module lookup or address-validation failure, perform no CRC read, discard the partial checksum and offset, retain the last successfully resolved identity, range, and baseline, and report the failure for retry on a later attempt.",,,,, User-Defined Memory Table,CS5000,"The CS Application shall provide a loadable User-Defined Memory Table containing at least 16 entries, each with the following parameters:",,,"To provide a configurable mechanism for defining and managing checksum parameters for user-defined memory regions, enabling flexible integrity checks.",Number of User-Defined Memory Table entries is configurable, ,,"1) ""Entry Checksum State""",,,,, ,,"2) ""Start Address""",,,,, diff --git a/docs/dox_src/cfs_cs.dox b/docs/dox_src/cfs_cs.dox index ef50320..288fb36 100644 --- a/docs/dox_src/cfs_cs.dox +++ b/docs/dox_src/cfs_cs.dox @@ -266,6 +266,22 @@ The format of a single entry in the Apps Results Table is defined by #CS_Res_App_Table_Entry_t. + + The ResourceID field records the ES identity of the application or library. + It precedes Name in the results entry; ground dump decoders must use the matching + results-table schema for this layout. + CS resolves this identity and the code range before each checksum chunk. If ES + reports a new identity, including a reload at the same address, CS discards the + partial checksum and old baseline and issues #CS_APP_RELOADED_INF_EID. The next + completed pass establishes the new instance's baseline. Initial discovery does + not issue a reload event. Changes to the code range under the same identity + restart the partial checksum but retain the baseline; a checksum mismatch alone + does not authorize a new baseline. + + An unavailable module is skipped without reading its cached address. CS retains + its last identity and baseline and discards partial progress before retrying. + Resolving module information does not prevent a concurrent unload between the + query and the checksum read. **/ /** diff --git a/eds/cs.xml b/eds/cs.xml index 9d83c97..601b402 100644 --- a/eds/cs.xml +++ b/eds/cs.xml @@ -181,6 +181,7 @@ + diff --git a/fsw/inc/cs_eventids.h b/fsw/inc/cs_eventids.h index c85bb9f..34d2b26 100644 --- a/fsw/inc/cs_eventids.h +++ b/fsw/inc/cs_eventids.h @@ -1997,6 +1997,20 @@ */ #define CS_VAL_TABLES_DEF_TBL_LONG_NAME_ERR_EID 155 +/** + * \brief CS Application Or Library Reload Baseline Reset Event ID + * + * \par Type: INFORMATION + * + * \par Cause: + * + * ES reports a different resource ID for a previously resolved application or + * library. CS discards its partial checksum and old baseline, and establishes a + * new baseline when checksumming the new instance completes. Initial discovery + * of an application or library does not generate this event. + */ +#define CS_APP_RELOADED_INF_EID 156 + /**@}*/ #endif diff --git a/fsw/src/cs_compute.c b/fsw/src/cs_compute.c index c3b7b64..0b1f143 100644 --- a/fsw/src/cs_compute.c +++ b/fsw/src/cs_compute.c @@ -295,7 +295,7 @@ CFE_Status_t CS_RefreshTableData(CS_LocalChecksumState_t *State, CFE_TBL_HandleI * Local helper function * *-----------------------------------------------------------------*/ -CFE_Status_t CS_RefreshAppSizeAndAddr(CS_LocalChecksumState_t *State, const char *AppName) +CFE_Status_t CS_RefreshAppSizeAndAddr(CS_LocalChecksumState_t *State, const char *AppName, CFE_ResourceId_t *ResolvedID) { CFE_ResourceId_t ResourceID; CFE_ES_AppInfo_t AppInfo; @@ -334,6 +334,7 @@ CFE_Status_t CS_RefreshAppSizeAndAddr(CS_LocalChecksumState_t *State, const char State->TotalSize = CFE_ES_MEMOFFSET_TO_SIZET(AppInfo.CodeSize); State->BufferAddr = CFE_ES_MEMADDRESS_TO_PTR(AppInfo.CodeAddress); State->CurrOffset = 0; + *ResolvedID = ResourceID; } else { @@ -473,29 +474,43 @@ CFE_Status_t CS_ComputeApp(CS_Res_App_Table_Entry_t *ResultsEntry, uint32 *Compu { CFE_Status_t Status; CS_LocalChecksumState_t State; + CFE_ResourceId_t ResourceID; + bool SameResource; memset(&State, 0, sizeof(State)); - State.BufferAddr = CFE_ES_MEMADDRESS_TO_PTR(ResultsEntry->StartAddress); - State.TotalSize = ResultsEntry->NumBytesToChecksum; - State.CurrOffset = ResultsEntry->ByteOffset; - - if (State.BufferAddr == NULL || CS_IsChecksumComplete(&State)) - { - Status = CS_RefreshAppSizeAndAddr(&State, ResultsEntry->Name); - } - else - { - /* Already got address and size, resume where left off */ - State.ResultBuffer = ResultsEntry->TempChecksumValue; - Status = CFE_SUCCESS; - } + /* Resolve every chunk: ES may reload an app between calls, even at the same address. + * This query does not prevent a concurrent unload while the chunk is being read. */ + Status = CS_RefreshAppSizeAndAddr(&State, ResultsEntry->Name, &ResourceID); if (Status == CFE_SUCCESS) { + SameResource = CFE_ResourceId_Equal(ResultsEntry->ResourceID, ResourceID); + + if (!SameResource && CFE_ResourceId_IsDefined(ResultsEntry->ResourceID)) + { + /* A new ES identity authorizes a new baseline; a CRC mismatch alone does not. */ + ResultsEntry->ComputedYet = false; + ResultsEntry->ComparisonValue = 0; + + CFE_EVS_SendEvent(CS_APP_RELOADED_INF_EID, + CFE_EVS_EventType_INFORMATION, + "App %s reloaded, baseline reset", + ResultsEntry->Name); + } + + if (SameResource && ResultsEntry->StartAddress == (cpuaddr)State.BufferAddr + && ResultsEntry->NumBytesToChecksum == State.TotalSize) + { + State.CurrOffset = ResultsEntry->ByteOffset; + State.ResultBuffer = ResultsEntry->TempChecksumValue; + } + /* A changed range under the same identity restarts the pass, retaining the baseline. */ + CS_DoChecksumChunk(&State); /* Export all values to resume next time */ + ResultsEntry->ResourceID = ResourceID; ResultsEntry->TempChecksumValue = State.ResultBuffer; ResultsEntry->StartAddress = (cpuaddr)State.BufferAddr; ResultsEntry->ByteOffset = State.CurrOffset; @@ -507,6 +522,13 @@ CFE_Status_t CS_ComputeApp(CS_Res_App_Table_Entry_t *ResultsEntry, uint32 *Compu /* Check against the previous value, if applicable */ Status = CS_CheckOrUpdateReference(&State, &ResultsEntry->ComputedYet, &ResultsEntry->ComparisonValue); } /* end if got module id ok */ + else + { + /* Retry from the beginning after an unresolved interval. Retain the last identity + * and baseline so the next successful lookup can detect a reload. */ + ResultsEntry->ByteOffset = 0; + ResultsEntry->TempChecksumValue = 0; + } *DoneWithEntry = CS_IsChecksumComplete(&State); diff --git a/fsw/src/cs_compute.h b/fsw/src/cs_compute.h index 834cec7..667c0d9 100644 --- a/fsw/src/cs_compute.h +++ b/fsw/src/cs_compute.h @@ -96,10 +96,15 @@ CFE_Status_t CS_ComputeTables(CS_Res_Tables_Table_Entry_t *ResultsEntry, uint32 * * \par Description * Computes checksums up to MaxBytesPerCycle bytes every call. This - * function is used to compute checksums for applications. + * function resolves the current application or library identity and code + * range on every call. A new ES resource ID discards the partial checksum + * and baseline and issues #CS_APP_RELOADED_INF_EID. The next completed pass + * establishes the baseline for the new instance. A range change under the + * same ID restarts the partial checksum without replacing the baseline. * * \par Assumptions, External Events, and Notes: - * None + * The ES information query does not prevent a concurrent module unload + * between resolving the code address and reading the checksum chunk. * * \param [in] ResultsEntry A pointer to the entry in a table * that we want to compute the diff --git a/unit-test/cs_compute_tests.c b/unit-test/cs_compute_tests.c index b2b0ec1..fe95c55 100644 --- a/unit-test/cs_compute_tests.c +++ b/unit-test/cs_compute_tests.c @@ -78,6 +78,104 @@ void CS_COMPUTE_TEST_CFE_ES_GetModuleInfoHandler2(void *UserObj, UT_EntryKey_t F AppInfo->AddressesAreValid = false; } +/* Module identity and code range supplied by ES for the reload regression tests. */ +#define CS_TEST_NUM_CRC_SAMPLES 8 +#define CS_COMPUTE_TEST_INITIAL_APP_ID 0x110001 +#define CS_COMPUTE_TEST_RELOADED_APP_ID 0x110002 +#define CS_COMPUTE_TEST_INITIAL_LIB_ID 0x120001 +#define CS_COMPUTE_TEST_RELOADED_LIB_ID 0x120002 +#define CS_COMPUTE_TEST_INITIAL_ADDR 1 +#define CS_COMPUTE_TEST_RELOCATED_ADDR 0x1000 + +typedef struct +{ + CFE_ResourceId_t ResourceID; + cpuaddr CodeAddress; + uint32 CodeSize; + bool AddressesAreValid; + bool ReloadAfterFirstChunk; +} CS_COMPUTE_TEST_Module_t; + +typedef struct +{ + const void *DataPtr; + size_t DataLength; + uint32 InputCRC; +} CS_COMPUTE_TEST_CrcSample_t; + +static CS_COMPUTE_TEST_CrcSample_t CS_COMPUTE_TEST_CrcSamples[CS_TEST_NUM_CRC_SAMPLES]; +static uint32 CS_COMPUTE_TEST_CrcCount; + +void CS_COMPUTE_TEST_CFE_ES_GetModuleIDHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) +{ + CS_COMPUTE_TEST_Module_t *Module = UserObj; + + if (FuncKey == UT_KEY(CFE_ES_GetAppIDByName)) + { + CFE_ES_AppId_t *AppIdPtr = UT_Hook_GetArgValueByName(Context, "AppIdPtr", CFE_ES_AppId_t *); + *AppIdPtr = CFE_ES_APPID_C(Module->ResourceID); + } + else + { + CFE_ES_LibId_t *LibIdPtr = UT_Hook_GetArgValueByName(Context, "LibIdPtr", CFE_ES_LibId_t *); + *LibIdPtr = CFE_ES_LIBID_C(Module->ResourceID); + } +} + +void CS_COMPUTE_TEST_CFE_ES_GetModuleInfoHandler_Current(void *UserObj, + UT_EntryKey_t FuncKey, + const UT_StubContext_t *Context) +{ + CS_COMPUTE_TEST_Module_t *Module = UserObj; + CFE_ES_AppInfo_t *AppInfo = UT_Hook_GetArgValueByName(Context, "ModuleInfo", CFE_ES_AppInfo_t *); + + memset(AppInfo, 0, sizeof(*AppInfo)); + AppInfo->ResourceId = Module->ResourceID; + AppInfo->CodeAddress = CFE_ES_MEMADDRESS_C(Module->CodeAddress); + AppInfo->CodeSize = CFE_ES_MEMOFFSET_C(Module->CodeSize); + AppInfo->AddressesAreValid = Module->AddressesAreValid; +} + +/* Record each actual CRC input, and optionally reload between recompute chunks. */ +void CS_COMPUTE_TEST_CFE_ES_CalculateCRCHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) +{ + CS_COMPUTE_TEST_Module_t *Module = UserObj; + uint32 Index = CS_COMPUTE_TEST_CrcCount++; + + if (Index < CS_TEST_NUM_CRC_SAMPLES) + { + CS_COMPUTE_TEST_CrcSamples[Index].DataPtr = UT_Hook_GetArgValueByName(Context, "DataPtr", const void *); + CS_COMPUTE_TEST_CrcSamples[Index].DataLength = UT_Hook_GetArgValueByName(Context, "DataLength", size_t); + CS_COMPUTE_TEST_CrcSamples[Index].InputCRC = UT_Hook_GetArgValueByName(Context, "InputCRC", uint32); + } + else + { + UtAssert_Failed("CRC sample buffer exhausted"); + } + + if (Module->ReloadAfterFirstChunk && Index == 0) + { + Module->ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_RELOADED_APP_ID); + Module->CodeAddress = CS_COMPUTE_TEST_RELOCATED_ADDR; + } +} + +void CS_COMPUTE_TEST_SetupModule(CS_COMPUTE_TEST_Module_t *Module) +{ + memset(Module, 0, sizeof(*Module)); + Module->ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_INITIAL_APP_ID); + Module->CodeAddress = CS_COMPUTE_TEST_INITIAL_ADDR; + Module->CodeSize = 5; + Module->AddressesAreValid = true; + + CS_COMPUTE_TEST_CrcCount = 0; + memset(CS_COMPUTE_TEST_CrcSamples, 0, sizeof(CS_COMPUTE_TEST_CrcSamples)); + UT_SetHandlerFunction(UT_KEY(CFE_ES_GetAppIDByName), CS_COMPUTE_TEST_CFE_ES_GetModuleIDHandler, Module); + UT_SetHandlerFunction(UT_KEY(CFE_ES_GetLibIDByName), CS_COMPUTE_TEST_CFE_ES_GetModuleIDHandler, Module); + UT_SetHandlerFunction(UT_KEY(CFE_ES_GetModuleInfo), CS_COMPUTE_TEST_CFE_ES_GetModuleInfoHandler_Current, Module); + UT_SetHandlerFunction(UT_KEY(CFE_ES_CalculateCRC), CS_COMPUTE_TEST_CFE_ES_CalculateCRCHandler, Module); +} + void CS_COMPUTE_TEST_CS_GetDefTblEntryByName(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { void **EntryPtr = UT_Hook_GetArgValueByName(Context, "EntryPtr", void **); @@ -1235,6 +1333,364 @@ void CS_ComputeApp_Test_OffsetlargerThanBytesCheck(void) UtAssert_UINT32_EQ(ResultsEntry.NumBytesToChecksum, CS_AppData.MaxBytesPerCycle); } +/* A relocated new app instance establishes a new baseline, once, rather than miscomparing. */ +void CS_ComputeApp_Test_RelocatedAppRebaseline(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ReloadedCRC = 7; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + CS_AppData.MaxBytesPerCycle = Module.CodeSize; + UT_SetDeferredRetcode(UT_KEY(CFE_ES_CalculateCRC), 1, InitialCRC); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, InitialCRC); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 0); + + Module.ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_RELOADED_APP_ID); + Module.CodeAddress = CS_COMPUTE_TEST_RELOCATED_ADDR; + UT_SetDeferredRetcode(UT_KEY(CFE_ES_CalculateCRC), 1, ReloadedCRC); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, ReloadedCRC); + UtAssert_UINT32_EQ(ComputedCSValue, ReloadedCRC); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[1].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[1].InputCRC, 0); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[0].EventID, CS_APP_RELOADED_INF_EID); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[0].EventType, CFE_EVS_EventType_INFORMATION); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 1); + + /* The same new instance is compared against its new baseline, without another event. */ + UT_SetDeferredRetcode(UT_KEY(CFE_ES_CalculateCRC), 1, ReloadedCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, ReloadedCRC); + UtAssert_STUB_COUNT(CFE_ES_GetModuleInfo, 3); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 1); +} + +/* A reload between chunks must never splice a cached old range into the new baseline. */ +void CS_ComputeApp_Test_RelocatedMidPassRestarts(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ReloadedCRC = 7; + const uint32 BytesPerCycle = 2; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + /* Keep two full chunks and a final partial chunk when the budget changes. */ + Module.CodeSize = 2 * BytesPerCycle + 1; + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), InitialCRC); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[0].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + UtAssert_BOOL_FALSE(ResultsEntry.ComputedYet); + + Module.ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_RELOADED_APP_ID); + Module.CodeAddress = CS_COMPUTE_TEST_RELOCATED_ADDR; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ReloadedCRC); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[1].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[1].InputCRC, 0); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, 0); + UtAssert_BOOL_FALSE(ResultsEntry.ComputedYet); + UtAssert_STUB_COUNT(CFE_ES_GetModuleInfo, 2); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[2].DataPtr, Module.CodeAddress + BytesPerCycle); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[2].InputCRC, ReloadedCRC); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, 2 * BytesPerCycle); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[3].DataPtr, Module.CodeAddress + 2 * BytesPerCycle); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[3].DataLength, Module.CodeSize - 2 * BytesPerCycle); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, ReloadedCRC); + UtAssert_STUB_COUNT(CFE_ES_GetModuleInfo, 4); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 1); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[0].EventID, CS_APP_RELOADED_INF_EID); +} + +/* Address and size reuse cannot hide a new app instance, including during a comparison pass. */ +void CS_ComputeApp_Test_SameAddressReloadRestarts(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ReloadedCRC = 7; + const uint32 BytesPerCycle = 2; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + /* Keep two full chunks and a final partial chunk when the budget changes. */ + Module.CodeSize = 2 * BytesPerCycle + 1; + CS_AppData.MaxBytesPerCycle = Module.CodeSize; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), InitialCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + Module.ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_RELOADED_APP_ID); + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ReloadedCRC); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[2].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[2].InputCRC, 0); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + UtAssert_BOOL_FALSE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, 0); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, ReloadedCRC); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 1); +} + +/* A CRC change alone remains a miscompare and must not authorize a baseline reset. */ +void CS_ComputeApp_Test_UnchangedInstanceCorruption(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ChangedCRC = 7; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + CS_AppData.MaxBytesPerCycle = Module.CodeSize; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), InitialCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ChangedCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CS_ERROR); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, InitialCRC); + UtAssert_UINT32_EQ(ComputedCSValue, ChangedCRC); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 0); +} + +/* Every chunk refreshes metadata while unchanged metadata preserves the CRC seed and offset. */ +void CS_ComputeApp_Test_UnchangedInstanceResumes(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 ExpectedCRC = 3; + const uint32 BytesPerCycle = 2; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + /* Keep two full chunks and a final partial chunk when the budget changes. */ + Module.CodeSize = 2 * BytesPerCycle + 1; + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ExpectedCRC); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[1].DataPtr, Module.CodeAddress + BytesPerCycle); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[1].InputCRC, ExpectedCRC); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, 2 * BytesPerCycle); + UtAssert_BOOL_FALSE(ResultsEntry.ComputedYet); + + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[2].DataPtr, Module.CodeAddress + 2 * BytesPerCycle); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[2].DataLength, Module.CodeSize - 2 * BytesPerCycle); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[2].InputCRC, ExpectedCRC); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, ExpectedCRC); + UtAssert_STUB_COUNT(CFE_ES_GetAppIDByName, 3); + UtAssert_STUB_COUNT(CFE_ES_GetModuleInfo, 3); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 0); +} + +/* A failed lookup retains the last identity and baseline, but never reads the cached range. */ +void CS_ComputeApp_Test_LookupFailureThenReload(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ReloadedCRC = 7; + const uint32 BytesPerCycle = 2; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + CFE_ResourceId_t OriginalID; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + /* Keep two full chunks and a final partial chunk when the budget changes. */ + Module.CodeSize = 2 * BytesPerCycle + 1; + OriginalID = Module.ResourceID; + CS_AppData.MaxBytesPerCycle = Module.CodeSize; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), InitialCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + + UT_SetDeferredRetcode(UT_KEY(CFE_ES_GetAppIDByName), 1, CFE_ES_ERR_NAME_NOT_FOUND); + UT_SetDeferredRetcode(UT_KEY(CFE_ES_GetLibIDByName), 1, CFE_ES_ERR_NAME_NOT_FOUND); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CS_ERR_NOT_FOUND); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_STUB_COUNT(CFE_ES_CalculateCRC, 2); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, OriginalID)); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, InitialCRC); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, 0); + UtAssert_UINT32_EQ(ResultsEntry.TempChecksumValue, 0); + UtAssert_ADDRESS_EQ(CFE_ES_MEMADDRESS_TO_PTR(ResultsEntry.StartAddress), Module.CodeAddress); + + Module.ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_RELOADED_APP_ID); + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ReloadedCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_BOOL_FALSE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, 0); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[2].InputCRC, 0); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 2); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[0].EventID, CS_COMPUTE_APP_ERR_EID); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[1].EventID, CS_APP_RELOADED_INF_EID); +} + +/* Invalid addresses during a transient failure cannot be used to finish a partial pass. */ +void CS_ComputeApp_Test_InvalidAddressThenRecovery(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 ExpectedCRC = 3; + const uint32 BytesPerCycle = 2; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + /* Keep two full chunks and a final partial chunk when the budget changes. */ + Module.CodeSize = 2 * BytesPerCycle + 1; + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ExpectedCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + + Module.AddressesAreValid = false; + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CS_ERR_NOT_FOUND); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_STUB_COUNT(CFE_ES_CalculateCRC, 1); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, 0); + UtAssert_UINT32_EQ(ResultsEntry.TempChecksumValue, 0); + + Module.AddressesAreValid = true; + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_FALSE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[1].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[1].InputCRC, 0); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 2); +} + +/* Mapping changes without a new ES identity restart the partial CRC, not the baseline. */ +void CS_ComputeApp_Test_SameInstanceMappingChange(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ChangedCRC = 7; + const uint32 BytesPerCycle = 2; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + /* Keep two full chunks and a final partial chunk when the budget changes. */ + Module.CodeSize = 2 * BytesPerCycle + 1; + CS_AppData.MaxBytesPerCycle = Module.CodeSize; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), InitialCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + + Module.CodeAddress = CS_COMPUTE_TEST_RELOCATED_ADDR; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ChangedCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[2].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[2].InputCRC, 0); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, BytesPerCycle); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, InitialCRC); + + /* A size-only change also restarts; its completed CRC is still compared with the old baseline. */ + Module.CodeSize = BytesPerCycle; + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CS_ERROR); + UtAssert_BOOL_TRUE(DoneWithEntry); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[3].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[3].InputCRC, 0); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, InitialCRC); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 0); +} + +/* Libraries use the same identity tracking through the ES library-name fallback. */ +void CS_ComputeApp_Test_LibraryReload(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 ReloadedCRC = 7; + uint32 ComputedCSValue = 0; + bool DoneWithEntry = false; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + Module.ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_INITIAL_LIB_ID); + CS_AppData.MaxBytesPerCycle = Module.CodeSize; + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_GetAppIDByName), CFE_ES_ERR_NAME_NOT_FOUND); + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), InitialCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 0); + + Module.ResourceID = CFE_ResourceId_FromInteger(CS_COMPUTE_TEST_RELOADED_LIB_ID); + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), ReloadedCRC); + UtAssert_INT32_EQ(CS_ComputeApp(&ResultsEntry, &ComputedCSValue, &DoneWithEntry), CFE_SUCCESS); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, ReloadedCRC); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_STUB_COUNT(CFE_ES_GetLibIDByName, 2); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 1); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[0].EventID, CS_APP_RELOADED_INF_EID); +} + void CS_ComputeApp_Test_EntryNotFinished(void) { CS_Res_App_Table_Entry_t ResultsEntry; @@ -1767,6 +2223,56 @@ void CS_RecomputeAppChildTask_Test_Nominal(void) UtAssert_STUB_COUNT(CS_SetDefEntryState, 2); } +/* A reload between child-task chunks restarts the recompute and restores enable states. */ +void CS_RecomputeAppChildTask_Test_ReloadDuringRecompute(void) +{ + CS_Res_App_Table_Entry_t ResultsEntry; + CS_Def_App_Table_Entry_t DefEntry; + CS_COMPUTE_TEST_Module_t Module; + const uint32 InitialCRC = 3; + const uint32 RecomputedCRC = 7; + const uint32 BytesPerCycle = 2; + uint32 ReloadedChunkCount; + + memset(&ResultsEntry, 0, sizeof(ResultsEntry)); + memset(&DefEntry, 0, sizeof(DefEntry)); + CS_COMPUTE_TEST_SetupModule(&Module); + ReloadedChunkCount = (Module.CodeSize + BytesPerCycle - 1) / BytesPerCycle; + ResultsEntry.State = CS_ChecksumState_ENABLED; + ResultsEntry.ComputedYet = true; + ResultsEntry.ComparisonValue = InitialCRC; + ResultsEntry.ResourceID = Module.ResourceID; + Module.ReloadAfterFirstChunk = true; + CS_AppData.RecomputeAppEntryPtr = &ResultsEntry; + CS_AppData.MaxBytesPerCycle = BytesPerCycle; + CS_AppData.HkPacket.Payload.RecomputeInProgress = true; + CS_Test_Setup_SetDefEntryState(CS_ChecksumState_ENABLED); + UT_SetHandlerFunction(UT_KEY(CS_GetAppDefTblEntryByName), CS_COMPUTE_TEST_CS_GetDefTblEntryByName, &DefEntry); + UT_SetDefaultReturnValue(UT_KEY(CFE_ES_CalculateCRC), RecomputedCRC); + + CS_RecomputeAppChildTask(); + + UtAssert_BOOL_FALSE(CS_AppData.HkPacket.Payload.RecomputeInProgress); + UtAssert_UINT32_EQ(ResultsEntry.State, CS_ChecksumState_ENABLED); + UtAssert_BOOL_TRUE(ResultsEntry.ComputedYet); + UtAssert_UINT32_EQ(ResultsEntry.ComparisonValue, RecomputedCRC); + UtAssert_UINT32_EQ(ResultsEntry.ByteOffset, Module.CodeSize); + UtAssert_BOOL_TRUE(CFE_ResourceId_Equal(ResultsEntry.ResourceID, Module.ResourceID)); + UtAssert_STUB_COUNT(CFE_ES_GetModuleInfo, 1 + ReloadedChunkCount); + UtAssert_STUB_COUNT(CFE_ES_CalculateCRC, 1 + ReloadedChunkCount); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[0].DataPtr, CS_COMPUTE_TEST_INITIAL_ADDR); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[1].DataPtr, Module.CodeAddress); + UtAssert_UINT32_EQ(CS_COMPUTE_TEST_CrcSamples[1].InputCRC, 0); + UtAssert_ADDRESS_EQ(CS_COMPUTE_TEST_CrcSamples[ReloadedChunkCount].DataPtr, + Module.CodeAddress + (ReloadedChunkCount - 1) * BytesPerCycle); + UtAssert_STUB_COUNT(CS_SetDefEntryState, 2); + CS_Test_Check_SetDefEntryState(&DefEntry, CS_ChecksumState_ENABLED); + UtAssert_STUB_COUNT(CFE_ES_ExitChildTask, 1); + UtAssert_STUB_COUNT(CFE_EVS_SendEvent, 2); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[0].EventID, CS_APP_RELOADED_INF_EID); + UtAssert_INT32_EQ(context_CFE_EVS_SendEvent[1].EventID, CS_RECOMPUTE_FINISH_APP_INF_EID); +} + void CS_RecomputeAppChildTask_Test_CouldNotGetAddress(void) { CS_Res_App_Table_Entry_t RecomputeAppEntry; @@ -2232,6 +2738,39 @@ void UtTest_Setup(void) CS_Test_Setup, CS_Test_TearDown, "CS_ComputeApp_Test_OffsetlargerThanBytesCheck"); + UtTest_Add(CS_ComputeApp_Test_RelocatedAppRebaseline, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_RelocatedAppRebaseline"); + UtTest_Add(CS_ComputeApp_Test_RelocatedMidPassRestarts, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_RelocatedMidPassRestarts"); + UtTest_Add(CS_ComputeApp_Test_SameAddressReloadRestarts, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_SameAddressReloadRestarts"); + UtTest_Add(CS_ComputeApp_Test_UnchangedInstanceCorruption, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_UnchangedInstanceCorruption"); + UtTest_Add(CS_ComputeApp_Test_UnchangedInstanceResumes, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_UnchangedInstanceResumes"); + UtTest_Add(CS_ComputeApp_Test_LookupFailureThenReload, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_LookupFailureThenReload"); + UtTest_Add(CS_ComputeApp_Test_InvalidAddressThenRecovery, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_InvalidAddressThenRecovery"); + UtTest_Add(CS_ComputeApp_Test_SameInstanceMappingChange, + CS_Test_Setup, + CS_Test_TearDown, + "CS_ComputeApp_Test_SameInstanceMappingChange"); + UtTest_Add(CS_ComputeApp_Test_LibraryReload, CS_Test_Setup, CS_Test_TearDown, "CS_ComputeApp_Test_LibraryReload"); UtTest_Add(CS_RecomputeEepromMemoryChildTask_Test_EEPROMTable, CS_Test_Setup, @@ -2266,6 +2805,10 @@ void UtTest_Setup(void) CS_Test_Setup, CS_Test_TearDown, "CS_RecomputeAppChildTask_Test_Nominal"); + UtTest_Add(CS_RecomputeAppChildTask_Test_ReloadDuringRecompute, + CS_Test_Setup, + CS_Test_TearDown, + "CS_RecomputeAppChildTask_Test_ReloadDuringRecompute"); UtTest_Add(CS_RecomputeAppChildTask_Test_CouldNotGetAddress, CS_Test_Setup, CS_Test_TearDown,