fprime/Svc/PrmDb/test/ut/PrmDbTester.cpp
Mishaal 0fc995fefd
Provide ability to update parameters via a file (#4165)
* Save point for PRM_SET_FILE work, add basic infrastruture

* fprime-format

* Create prime and backup DB, initial implementation of file based set, some UT updates

* More work on Prime and Backup DBs including helper methods, utilizing those for copy, and UTs

* spelling

* printf in Tester fix

* printf in Tester fix

* printf in Tester fix

* Remove a debug printf, update new method args, additional offNom Set File tests

* Spelling and format

* Clean up comments

* Sync with upstream devel updates

* Add additional UT for reverting parameter db on set file failure

* Spelling and format

* Updates to PrmDb after first review; change to active/staging design

* Spelling

* Remove FIXME comment

* Review fixes

* Fix UT

* Format

* Format II

* Format III

---------

Co-authored-by: M Starch <LeStarch@googlemail.com>
2025-10-14 13:40:27 -07:00

1399 lines
50 KiB
C++

/*
* PrmDbTester.cpp
*
* Created on: Mar 18, 2015
* Author: tcanham
*/
#include <gtest/gtest.h>
#include <Fw/Com/ComBuffer.hpp>
#include <Fw/Com/ComPacket.hpp>
#include <Fw/Test/UnitTest.hpp>
#include <Os/Delegate.hpp>
#include <Os/Stub/test/File.hpp>
#include <Svc/PrmDb/test/ut/PrmDbTester.hpp>
#include <cstdio>
#include "Os/Stub/Directory.hpp"
#include "Os/Stub/FileSystem.hpp"
namespace Svc {
typedef PrmDb_PrmWriteError PrmWriteError;
typedef PrmDb_PrmReadError PrmReadError;
void PrmDbTester::runNominalPopulate() {
// clear database
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
// build a test parameter value with a simple value
U32 val = 0x10;
FwPrmIdType id = 0x21;
Fw::ParamBuffer pBuff;
Fw::SerializeStatus stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
static bool pdb003 = false;
if (not pdb003) {
REQUIREMENT("PDB-003");
pdb003 = true;
}
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
ASSERT_EVENTS_PrmIdAdded(0, id);
// retrieve it
U32 testVal;
static bool pdb002 = false;
if (not pdb002) {
REQUIREMENT("PDB-002");
pdb002 = true;
}
this->invoke_to_getPrm(0, id, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, val);
// update the value
val = 0x15;
pBuff.resetSer();
stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// retrieve it
pBuff.resetSer();
testVal = 0;
this->invoke_to_getPrm(0, id, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, val);
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdUpdated_SIZE(1);
ASSERT_EVENTS_PrmIdUpdated(0, id);
// add a new entry
id = 0x25;
val = 0x30;
pBuff.resetSer();
stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
ASSERT_EVENTS_PrmIdAdded(0, id);
}
void PrmDbTester::runNominalSaveFile() {
Os::Stub::File::Test::StaticData::setWriteResult(m_io_data, sizeof m_io_data);
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
// fill with data
this->runNominalPopulate();
// save the data
this->clearEvents();
this->clearHistory();
static bool pdb004 = false;
if (not pdb004) {
REQUIREMENT("PDB-004");
pdb004 = true;
}
this->sendCmd_PRM_SAVE_FILE(0, 12);
Fw::QueuedComponentBase::MsgDispatchStatus stat = this->m_impl.doDispatch();
EXPECT_EQ(stat, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12, Fw::CmdResponse::OK);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmFileSaveComplete_SIZE(1);
ASSERT_EVENTS_PrmFileSaveComplete(0, 2);
}
void PrmDbTester::runNominalLoadFile() {
// Preconditions to populate the write file
this->runNominalSaveFile();
Os::Stub::File::Test::StaticData::setReadResult(m_io_data, Os::Stub::File::Test::StaticData::data.pointer);
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
// save the data
this->clearEvents();
static bool pdb001 = false;
if (not pdb001) {
REQUIREMENT("PDB-001");
pdb001 = true;
}
this->m_impl.readParamFile();
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmFileLoadComplete_SIZE(1);
ASSERT_EVENTS_PrmFileLoadComplete(0, "ACTIVE", 2, 2, 0);
// verify values (populated by runNominalPopulate())
// first
Fw::ParamBuffer pBuff;
U32 testVal;
this->invoke_to_getPrm(0, 0x21, pBuff);
// deserialize it
Fw::SerializeStatus stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, 0x15u);
// second
pBuff.resetSer();
this->invoke_to_getPrm(0, 0x25, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, 0x30u);
}
void PrmDbTester::runMissingExtraParams() {
// load up database
this->runNominalPopulate();
// ask for ID that isn't present
this->clearEvents();
Fw::ParamBuffer pBuff;
EXPECT_EQ(Fw::ParamValid::INVALID, this->invoke_to_getPrm(0, 0x1000, pBuff).e);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdNotFound_SIZE(1);
ASSERT_EVENTS_PrmIdNotFound(0, 0x1000);
// clear database
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
this->clearEvents();
// write too many entries
for (FwPrmIdType entry = 0; entry <= PRMDB_NUM_DB_ENTRIES; entry++) {
EXPECT_EQ(Fw::FW_SERIALIZE_OK, pBuff.serializeFrom(static_cast<U32>(10)));
this->invoke_to_setPrm(0, entry, pBuff);
// dispatch message
this->m_impl.doDispatch();
}
ASSERT_EVENTS_SIZE(PRMDB_NUM_DB_ENTRIES + 1);
ASSERT_EVENTS_PrmIdAdded_SIZE(PRMDB_NUM_DB_ENTRIES);
ASSERT_EVENTS_PrmDbFull_SIZE(1);
ASSERT_EVENTS_PrmDbFull(0, PRMDB_NUM_DB_ENTRIES);
}
void PrmDbTester::runRefPrmFile() {
{
// ID = 00x1000
U32 val = 14;
FwPrmIdType id = 0x1000;
Fw::ParamBuffer pBuff;
Fw::SerializeStatus stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
ASSERT_EVENTS_PrmIdAdded(0, id);
// retrieve it
U32 testVal;
this->invoke_to_getPrm(0, id, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, val);
}
{
// ID = 0x1001
I16 val = 15;
FwPrmIdType id = 0x1001;
Fw::ParamBuffer pBuff;
Fw::SerializeStatus stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
ASSERT_EVENTS_PrmIdAdded(0, id);
// retrieve it
I16 testVal;
this->invoke_to_getPrm(0, id, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, val);
}
{
// ID = 0x1100
U8 val = 32;
FwPrmIdType id = 0x1100;
Fw::ParamBuffer pBuff;
Fw::SerializeStatus stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
ASSERT_EVENTS_PrmIdAdded(0, id);
// retrieve it
U8 testVal;
this->invoke_to_getPrm(0, id, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, val);
}
{
// ID = 0x1101
F32 val = 33.34;
FwPrmIdType id = 0x1101;
Fw::ParamBuffer pBuff;
Fw::SerializeStatus stat = pBuff.serializeFrom(val);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// clear all events
this->clearEvents();
// send it to the database
this->invoke_to_setPrm(0, id, pBuff);
// dispatch message
this->m_impl.doDispatch();
// Verify event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
ASSERT_EVENTS_PrmIdAdded(0, id);
// retrieve it
F32 testVal;
this->invoke_to_getPrm(0, id, pBuff);
// deserialize it
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, val);
}
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_SAVE_FILE(0, 12);
Fw::QueuedComponentBase::MsgDispatchStatus mstat = this->m_impl.doDispatch();
EXPECT_EQ(mstat, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12, Fw::CmdResponse::OK);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmFileSaveComplete_SIZE(1);
ASSERT_EVENTS_PrmFileSaveComplete(0, 4);
}
void PrmDbTester::runFileReadError() {
// Preconditions setup and test
this->runNominalLoadFile();
this->clearEvents();
// Loop through all size errors testing each
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
this->m_errorType = FILE_SIZE_ERROR;
for (FwSizeType i = 0; i < 4; i++) {
clearEvents();
this->m_waits = i;
this->m_impl.readParamFile();
ASSERT_EVENTS_SIZE(1);
switch (i) {
case 0:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::DELIMITER_SIZE, 0, sizeof(U8) + 1);
break;
case 1:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::RECORD_SIZE_SIZE, 0, sizeof(U32) + 1);
break;
case 2:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_ID_SIZE, 0, sizeof(FwPrmIdType) + 1);
break;
case 3:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_VALUE_SIZE, 0, sizeof(U32) + 1);
break;
default:
FAIL() << "Reached unknown case";
}
}
// Loop through failure statuses
for (FwSizeType i = 0; i < 2; i++) {
this->m_errorType = FILE_STATUS_ERROR;
// Set various file errors
switch (i) {
case 0:
this->m_status = Os::File::Status::DOESNT_EXIST;
break;
case 1:
this->m_status = Os::File::Status::NOT_OPENED;
break;
default:
FAIL() << "Reached unknown case";
}
// Loop through various field reads
for (FwSizeType j = 0; j < 4; j++) {
clearEvents();
this->m_waits = j;
this->m_impl.readParamFile();
ASSERT_EVENTS_SIZE(1);
switch (j) {
case 0:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::DELIMITER, 0, this->m_status);
break;
case 1:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::RECORD_SIZE, 0, this->m_status);
break;
case 2:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_ID, 0, this->m_status);
break;
case 3:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_VALUE, 0, this->m_status);
break;
default:
FAIL() << "Reached unknown case";
}
}
}
this->m_errorType = FILE_DATA_ERROR;
for (FwSizeType i = 0; i < 2; i++) {
clearEvents();
this->m_waits = i;
this->m_impl.readParamFile();
ASSERT_EVENTS_SIZE(1);
switch (i) {
case 0:
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
// Parameter read error caused by adding one to the expected read
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::DELIMITER_VALUE, 0, PRMDB_ENTRY_DELIMITER + 1);
break;
case 1: {
// Data in this test is corrupted by adding 1 to the first data byte read. Since data is stored in
// big-endian format the highest order byte of the record size (U32) must have one added to it.
// Expected result of '8' inherited from original design of test.
U32 expected_error_value = sizeof(FwPrmIdType) + 4 + (1 << ((sizeof(U32) - 1) * 8));
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::RECORD_SIZE_VALUE, 0, expected_error_value);
break;
}
default:
FAIL() << "Reached unknown case";
}
}
}
void PrmDbTester::runFileWriteError() {
// File open error
this->clearEvents();
// register interceptor
Os::Stub::File::Test::StaticData::setWriteResult(m_io_data, sizeof m_io_data);
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::DOESNT_EXIST);
// dispatch command
this->sendCmd_PRM_SAVE_FILE(0, 12);
Fw::QueuedComponentBase::MsgDispatchStatus stat = this->m_impl.doDispatch();
ASSERT_EQ(stat, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// check for failed event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::OPEN, 0, Os::File::DOESNT_EXIST);
// check command status
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12, Fw::CmdResponse::EXECUTION_ERROR);
this->runNominalPopulate();
// Loop through all size errors testing each
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
this->m_errorType = FILE_SIZE_ERROR;
for (FwSizeType i = 0; i < 4; i++) {
clearEvents();
this->clearHistory();
this->m_waits = i;
this->sendCmd_PRM_SAVE_FILE(0, 12);
stat = this->m_impl.doDispatch();
ASSERT_EQ(stat, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
ASSERT_EVENTS_SIZE(1);
switch (i) {
case 0:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::DELIMITER_SIZE, 0, sizeof(U8) + 1);
break;
case 1:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::RECORD_SIZE_SIZE, 0, sizeof(U32) + 1);
break;
case 2:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_ID_SIZE, 0, sizeof(FwPrmIdType) + 1);
break;
case 3:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_VALUE_SIZE, 0, sizeof(U32) + 1);
break;
default:
FAIL() << "Reached unknown case";
}
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12, Fw::CmdResponse::EXECUTION_ERROR);
}
// Loop through failure statuses
for (FwSizeType i = 0; i < 2; i++) {
this->m_errorType = FILE_STATUS_ERROR;
// Set various file errors
switch (i) {
case 0:
this->m_status = Os::File::Status::DOESNT_EXIST;
break;
case 1:
this->m_status = Os::File::Status::NOT_OPENED;
break;
default:
FAIL() << "Reached unknown case";
}
// Loop through various field reads
for (FwSizeType j = 0; j < 4; j++) {
clearEvents();
this->clearHistory();
this->m_waits = j;
this->sendCmd_PRM_SAVE_FILE(0, 12);
stat = this->m_impl.doDispatch();
ASSERT_EQ(stat, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
ASSERT_EVENTS_SIZE(1);
switch (j) {
case 0:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::DELIMITER, 0, this->m_status);
break;
case 1:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::RECORD_SIZE, 0, this->m_status);
break;
case 2:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_ID, 0, this->m_status);
break;
case 3:
ASSERT_EVENTS_PrmFileWriteError_SIZE(1);
ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_VALUE, 0, this->m_status);
break;
default:
FAIL() << "Reached unknown case";
}
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12, Fw::CmdResponse::EXECUTION_ERROR);
}
}
}
void PrmDbTester::runDbEqualTest() {
Fw::SerializeStatus serStat;
// 1. Test with empty databases - should be equal
this->m_impl.clearDb(PrmDb_PrmDbType::DB_ACTIVE);
this->m_impl.clearDb(PrmDb_PrmDbType::DB_STAGING);
EXPECT_TRUE(this->m_impl.dbEqual());
// 2. Add an entry to active DB only - should not be equal
U32 val1 = 0x42;
FwPrmIdType id1 = 0x100;
Fw::ParamBuffer pBuff;
serStat = pBuff.serializeFrom(val1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id1, pBuff, PrmDb_PrmDbType::DB_ACTIVE);
EXPECT_FALSE(this->m_impl.dbEqual());
// 3. Add same entry to staging DB - should be equal again
pBuff.resetSer();
serStat = pBuff.serializeFrom(val1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id1, pBuff, PrmDb_PrmDbType::DB_STAGING);
EXPECT_TRUE(this->m_impl.dbEqual());
// 4. Update entry in active DB only - should not be equal
U32 val2 = 0x43;
pBuff.resetSer();
serStat = pBuff.serializeFrom(val2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id1, pBuff, PrmDb_PrmDbType::DB_STAGING);
EXPECT_FALSE(this->m_impl.dbEqual());
// 5. Update staging DB to match - should be equal again
pBuff.resetSer();
serStat = pBuff.serializeFrom(val2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id1, pBuff, PrmDb_PrmDbType::DB_ACTIVE);
EXPECT_TRUE(this->m_impl.dbEqual());
// 6. Add different entry to staging DB - should not be equal
U32 val3 = 0x44;
FwPrmIdType id2 = 0x101;
pBuff.resetSer();
serStat = pBuff.serializeFrom(val3);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id2, pBuff, PrmDb_PrmDbType::DB_STAGING);
EXPECT_FALSE(this->m_impl.dbEqual());
}
void PrmDbTester::runDbCopyTest() {
Fw::SerializeStatus serStat;
// Clear both databases
this->m_impl.clearDb(PrmDb_PrmDbType::DB_ACTIVE);
this->m_impl.clearDb(PrmDb_PrmDbType::DB_STAGING);
// Add entries to active DB only
U32 val1 = 0x1234;
FwPrmIdType id1 = 0x100;
Fw::ParamBuffer pBuff;
// Add first parameter
pBuff.resetSer();
serStat = pBuff.serializeFrom(val1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id1, pBuff, PrmDb_PrmDbType::DB_ACTIVE);
// Add second parameter
F32 val2 = 3.14159f;
FwPrmIdType id2 = 0x200;
pBuff.resetSer();
serStat = pBuff.serializeFrom(val2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id2, pBuff, PrmDb_PrmDbType::DB_ACTIVE);
// Verify databases are not equal
EXPECT_FALSE(this->m_impl.dbEqual());
// Copy active DB to staging DB
this->m_impl.dbCopy(PrmDb_PrmDbType::DB_STAGING, PrmDb_PrmDbType::DB_ACTIVE);
// Verify databases are now equal
EXPECT_TRUE(this->m_impl.dbEqual());
// Verify values in the staging DB
pBuff.resetSer();
U32 testVal1;
FwSizeType idx = 0;
// Find the parameter and get its index
for (FwSizeType i = 0; i < PRMDB_NUM_DB_ENTRIES; i++) {
if (this->m_impl.m_activeDb[i].used && this->m_impl.m_stagingDb[i].id == id1) {
idx = i;
break;
}
}
EXPECT_TRUE(this->m_impl.m_activeDb[idx].used);
EXPECT_EQ(id1, this->m_impl.m_stagingDb[idx].id);
pBuff = this->m_impl.m_stagingDb[idx].val;
serStat = pBuff.deserializeTo(testVal1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
EXPECT_EQ(val1, testVal1);
// Clear both databases
this->m_impl.clearDb(PrmDb_PrmDbType::DB_ACTIVE);
this->m_impl.clearDb(PrmDb_PrmDbType::DB_STAGING);
// Add different entries to active and staging DBs
// active DB - add first parameter
pBuff.resetSer();
serStat = pBuff.serializeFrom(val1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id1, pBuff, PrmDb_PrmDbType::DB_ACTIVE);
// active DB - add second parameter
pBuff.resetSer();
serStat = pBuff.serializeFrom(val2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id2, pBuff, PrmDb_PrmDbType::DB_ACTIVE);
// staging DB - add different parameter
U16 val3 = 0x5678;
FwPrmIdType id3 = 0x300;
pBuff.resetSer();
serStat = pBuff.serializeFrom(val3);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
this->m_impl.updateAddPrmImpl(id3, pBuff, PrmDb_PrmDbType::DB_STAGING);
// Verify databases are not equal
EXPECT_FALSE(this->m_impl.dbEqual());
// Copy only the second entry from active to staging at the same index
FwSizeType activeIdx2 = 1; // Index of second entry in active DB
this->m_impl.dbCopySingle(PrmDb_PrmDbType::DB_STAGING, PrmDb_PrmDbType::DB_ACTIVE, activeIdx2);
// Verify the specific entry was copied correctly
EXPECT_TRUE(this->m_impl.m_stagingDb[activeIdx2].used);
EXPECT_EQ(id2, this->m_impl.m_stagingDb[activeIdx2].id);
// Verify value matches
pBuff = this->m_impl.m_stagingDb[activeIdx2].val;
F32 testVal2;
serStat = pBuff.deserializeTo(testVal2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
EXPECT_EQ(val2, testVal2);
// Verify the original entry in staging DB is still there
bool foundOriginal = false;
for (FwSizeType i = 0; i < PRMDB_NUM_DB_ENTRIES; i++) {
if (this->m_impl.m_stagingDb[i].used && this->m_impl.m_stagingDb[i].id == id3) {
foundOriginal = true;
// Verify value is still correct
pBuff = this->m_impl.m_stagingDb[i].val;
U16 testVal3;
serStat = pBuff.deserializeTo(testVal3);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, serStat);
EXPECT_EQ(val3, testVal3);
break;
}
}
EXPECT_TRUE(foundOriginal);
// Databases should still not be equal since we only copied one entry
EXPECT_FALSE(this->m_impl.dbEqual());
}
void PrmDbTester::runDbCommitTest() {
// 1. Set the m_state to FILE_UPDATES_STAGED
this->m_impl.m_state = PrmDbFileLoadState::FILE_UPDATES_STAGED;
// 2. Populate both databases with different content
// Clear both databases first
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
this->m_impl.clearDb(PrmDbType::DB_STAGING);
// Add parameters to active database
U32 activeVal1 = 0x1234;
FwPrmIdType activeId1 = 0x100;
Fw::ParamBuffer pBuff;
Fw::SerializeStatus stat = pBuff.serializeFrom(activeVal1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(activeId1, pBuff, PrmDbType::DB_ACTIVE);
// Add different parameters to staging database
U32 stagingVal1 = 0x5678;
FwPrmIdType stagingId1 = 0x100; // Same ID but different value
pBuff.resetSer();
stat = pBuff.serializeFrom(stagingVal1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(stagingId1, pBuff, PrmDbType::DB_STAGING);
// Add a parameter that's only in the staging database
F32 stagingVal2 = 3.14159f;
FwPrmIdType stagingId2 = 0x200;
pBuff.resetSer();
stat = pBuff.serializeFrom(stagingVal2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(stagingId2, pBuff, PrmDbType::DB_STAGING);
// Store pointers to databases before swap for verification
PrmDbImpl::t_dbStruct* preSwapActiveDb = this->m_impl.m_activeDb;
PrmDbImpl::t_dbStruct* preSwapStagingDb = this->m_impl.m_stagingDb;
// Clear events and command history
this->clearEvents();
this->clearHistory();
// 3. Execute the commit command
this->sendCmd_PRM_COMMIT_STAGED(0, 10);
Fw::QueuedComponentBase::MsgDispatchStatus dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// 4. Verify results
// Check command response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_COMMIT_STAGED, 10, Fw::CmdResponse::OK);
// Check event
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbCommitComplete_SIZE(1);
// Verify the state is now IDLE
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::IDLE);
// Verify that the database pointers have been swapped
EXPECT_EQ(this->m_impl.m_activeDb, preSwapStagingDb);
EXPECT_EQ(this->m_impl.m_stagingDb, preSwapActiveDb);
// Verify that the new staging database is empty
bool allEntriesCleared = true;
for (FwSizeType i = 0; i < PRMDB_NUM_DB_ENTRIES; i++) {
if (this->m_impl.m_stagingDb[i].used) {
allEntriesCleared = false;
break;
}
}
EXPECT_TRUE(allEntriesCleared);
// Verify that parameters can be accessed from the newly active database
// (which was formerly the staging database)
pBuff.resetSer();
U32 retrievedVal1;
this->invoke_to_getPrm(0, stagingId1, pBuff);
stat = pBuff.deserializeTo(retrievedVal1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(retrievedVal1, stagingVal1);
pBuff.resetSer();
F32 retrievedVal2;
this->invoke_to_getPrm(0, stagingId2, pBuff);
stat = pBuff.deserializeTo(retrievedVal2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(retrievedVal2, stagingVal2);
// Test invalid state handling - try to commit again when not in FILE_UPDATES_STAGED state
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_COMMIT_STAGED(0, 11);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Should get validation error and warning event
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_COMMIT_STAGED, 11, Fw::CmdResponse::VALIDATION_ERROR);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
}
void PrmDbTester::runPrmFileLoadNominal() {
Fw::String file = "TestFile.prm";
Fw::QueuedComponentBase::MsgDispatchStatus dispatchStatus;
// Store pointers to databases before swap for verification
PrmDbImpl::t_dbStruct* preSwapActiveDb = this->m_impl.m_activeDb;
PrmDbImpl::t_dbStruct* preSwapStagingDb = this->m_impl.m_stagingDb;
// Ensure we're in IDLE state
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::IDLE);
// Populate the active DB and save to file
runNominalSaveFile();
printf("Saved into File: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Clear both databases
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
this->m_impl.clearDb(PrmDbType::DB_STAGING);
printf("Cleared: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Populate active database with some values so we can test merge=true
// A new ID
U32 activeVal1 = 0x1234;
FwPrmIdType activeId1 = 0x100;
Fw::ParamBuffer pBuff;
// Add parameter to active database
Fw::SerializeStatus stat = pBuff.serializeFrom(activeVal1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(activeId1, pBuff, PrmDbType::DB_ACTIVE);
// Verify parameter is in active database
pBuff.resetSer();
U32 testVal;
this->invoke_to_getPrm(0, activeId1, pBuff);
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, activeVal1);
// A existing (in file) ID
U32 activeVal2Original = 0x30;
U32 activeVal2Update = activeVal2Original + 1;
FwPrmIdType activeId2 = 0x25; // This ID exists in the file with a different value
pBuff.resetSer();
// Add parameter to active database
stat = pBuff.serializeFrom(activeVal2Update);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(activeId2, pBuff, PrmDbType::DB_ACTIVE);
// Verify parameter is in active database
pBuff.resetSer();
U32 testVal2;
this->invoke_to_getPrm(0, activeId2, pBuff);
stat = pBuff.deserializeTo(testVal2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal2, activeVal2Update);
printf("Added new: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Send PRM_LOAD_FILE command with merge=true to merge with active database
Os::Stub::File::Test::StaticData::setReadResult(m_io_data, Os::Stub::File::Test::StaticData::data.pointer);
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_LOAD_FILE(0, 10, file, PrmDb_Merge::MERGE);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
ASSERT_EVENTS_SIZE(2);
// Verify EVRs for copy (because we are merging)
ASSERT_EVENTS_PrmDbCopyAllComplete_SIZE(1);
ASSERT_EVENTS_PrmDbCopyAllComplete(0, "ACTIVE", "STAGING");
// Verify EVRs for the file load
ASSERT_EVENTS_PrmFileLoadComplete_SIZE(1);
ASSERT_EVENTS_PrmFileLoadComplete(0, "STAGING", 2, 1, 1);
printf("Parameter Load file complete: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Verify state and command response after PRM_LOAD_FILE
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::FILE_UPDATES_STAGED);
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_LOAD_FILE, 10, Fw::CmdResponse::OK);
// Verify that parameters in staging database have expected values
for (FwSizeType i = 0; i < PRMDB_NUM_DB_ENTRIES; i++) {
// Check for the parameter that we added after the save file (since we are merging)
if (this->m_impl.m_stagingDb[i].used && this->m_impl.m_stagingDb[i].id == activeId1) {
pBuff = this->m_impl.m_stagingDb[i].val;
U32 checkVal;
stat = pBuff.deserializeTo(checkVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(checkVal, activeVal1);
}
// Check for the parameter that we updated after the save file (since we are merging)
if (this->m_impl.m_stagingDb[i].used && this->m_impl.m_stagingDb[i].id == activeId2) {
pBuff = this->m_impl.m_stagingDb[i].val;
U32 checkVal;
stat = pBuff.deserializeTo(checkVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(checkVal, activeVal2Original); // Value should match what was in the file, not what we set
}
}
// Send PRM_COMMIT_STAGED command
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_COMMIT_STAGED(0, 11);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify state and command response after PRM_COMMIT_STAGED
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::IDLE);
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_COMMIT_STAGED, 11, Fw::CmdResponse::OK);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbCommitComplete_SIZE(1);
// Verify the database pointers have been swapped
EXPECT_EQ(this->m_impl.m_activeDb, preSwapStagingDb);
EXPECT_EQ(this->m_impl.m_stagingDb, preSwapActiveDb);
// Verify the new staging database (former active) is empty
bool allEntriesCleared = true;
for (FwSizeType i = 0; i < PRMDB_NUM_DB_ENTRIES; i++) {
if (this->m_impl.m_stagingDb[i].used) {
allEntriesCleared = false;
break;
}
}
EXPECT_TRUE(allEntriesCleared);
// Verify we can now perform operations only allowed in IDLE state
// Try setting a parameter - should work in IDLE state
U32 newVal3 = 0x9ABC;
FwPrmIdType newId3 = 0x300;
pBuff.resetSer();
stat = pBuff.serializeFrom(newVal3);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->clearEvents();
this->invoke_to_setPrm(0, newId3, pBuff);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify parameter was added
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
// Verify we can retrieve the newly set parameter
pBuff.resetSer();
U32 retrievedVal3;
Fw::ParamValid valid = this->invoke_to_getPrm(0, newId3, pBuff);
EXPECT_EQ(Fw::ParamValid::VALID, valid);
stat = pBuff.deserializeTo(retrievedVal3);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(retrievedVal3, newVal3);
}
void PrmDbTester::runPrmFileLoadWithErrors() {
Fw::String file = "TestFile.prm";
Fw::QueuedComponentBase::MsgDispatchStatus dispatchStatus;
// Store pointers to databases before swap for verification
PrmDbImpl::t_dbStruct* preSwapActiveDb = this->m_impl.m_activeDb;
PrmDbImpl::t_dbStruct* preSwapStagingDb = this->m_impl.m_stagingDb;
// Ensure we're in IDLE state
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::IDLE);
// Populate the active DB and save to file
runNominalSaveFile();
printf("Saved into File: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Clear both databases
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
this->m_impl.clearDb(PrmDbType::DB_STAGING);
printf("Cleared: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Populate active database with some values so we can test merge=true
// A new ID
U32 activeVal1 = 0x1234;
FwPrmIdType activeId1 = 0x100;
Fw::ParamBuffer pBuff;
// Add parameter to active database
Fw::SerializeStatus stat = pBuff.serializeFrom(activeVal1);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(activeId1, pBuff, PrmDbType::DB_ACTIVE);
// Verify parameter is in active database
pBuff.resetSer();
U32 testVal;
this->invoke_to_getPrm(0, activeId1, pBuff);
stat = pBuff.deserializeTo(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal, activeVal1);
// A existing (in file) ID
U32 activeVal2Original = 0x30;
U32 activeVal2Update = activeVal2Original + 1;
FwPrmIdType activeId2 = 0x25; // This ID exists in the file with a different value
pBuff.resetSer();
// Add parameter to active database
stat = pBuff.serializeFrom(activeVal2Update);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
this->m_impl.updateAddPrmImpl(activeId2, pBuff, PrmDbType::DB_ACTIVE);
// Verify parameter is in active database
pBuff.resetSer();
U32 testVal2;
this->invoke_to_getPrm(0, activeId2, pBuff);
stat = pBuff.deserializeTo(testVal2);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
EXPECT_EQ(testVal2, activeVal2Update);
printf("Added new: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Send PRM_LOAD_FILE command with merge=true to merge with active database
// but with a file open error
Os::Stub::File::Test::StaticData::setReadResult(m_io_data, Os::Stub::File::Test::StaticData::data.pointer);
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::DOESNT_EXIST);
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_LOAD_FILE(0, 10, file, PrmDb_Merge::MERGE);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
ASSERT_EVENTS_SIZE(3);
// Verify EVRs for copy (because we are merging)
ASSERT_EVENTS_PrmDbCopyAllComplete_SIZE(1);
// Verify EVRs for file load read error
ASSERT_EVENTS_PrmFileReadError_SIZE(1);
// Verify EVRs for the file load cmd failure
ASSERT_EVENTS_PrmDbFileLoadFailed_SIZE(1);
printf("Parameter Load file complete: \n");
printDb(PrmDbType::DB_ACTIVE);
printDb(PrmDbType::DB_STAGING);
// Verify state and command response after PRM_LOAD_FILE
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::IDLE);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_LOAD_FILE, 10, Fw::CmdResponse::EXECUTION_ERROR);
// Verify the database pointers have NOT been swapped
EXPECT_EQ(this->m_impl.m_activeDb, preSwapActiveDb);
EXPECT_EQ(this->m_impl.m_stagingDb, preSwapStagingDb);
// Verify the staging database is empty
bool allEntriesCleared = true;
for (FwSizeType i = 0; i < PRMDB_NUM_DB_ENTRIES; i++) {
if (this->m_impl.m_stagingDb[i].used) {
allEntriesCleared = false;
break;
}
}
EXPECT_TRUE(allEntriesCleared);
}
void PrmDbTester::runPrmFileLoadIllegal() {
Fw::QueuedComponentBase::MsgDispatchStatus dispatchStatus;
Fw::ParamBuffer pBuff;
U32 testVal = 0x1234;
FwPrmIdType testId = 0x42;
// Serialize test value for parameter setting
Fw::SerializeStatus stat = pBuff.serializeFrom(testVal);
EXPECT_EQ(Fw::FW_SERIALIZE_OK, stat);
// -------------------------------------------------------------------
// 1. Test illegal operations in LOADING_FILE_UPDATES state
// -------------------------------------------------------------------
this->m_impl.m_state = PrmDbFileLoadState::LOADING_FILE_UPDATES;
// 1.1 Attempt PRM_LOAD_FILE while already loading
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_LOAD_FILE(0, 10, Fw::String("file.prm"), PrmDb_Merge::MERGE);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_LOAD_FILE, 10, Fw::CmdResponse::BUSY);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::LOADING_FILE_UPDATES,
PrmDb_PrmLoadAction::LOAD_FILE_COMMAND);
// 1.2 Attempt PRM_SAVE_FILE during loading
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_SAVE_FILE(0, 11);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 11, Fw::CmdResponse::BUSY);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::LOADING_FILE_UPDATES,
PrmDb_PrmLoadAction::SAVE_FILE_COMMAND);
// 1.3 Attempt to set parameter during loading
this->clearEvents();
this->clearHistory();
this->invoke_to_setPrm(0, testId, pBuff);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response (warning event only, no added event)
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::LOADING_FILE_UPDATES,
PrmDb_PrmLoadAction::SET_PARAMETER);
ASSERT_EVENTS_PrmIdAdded_SIZE(0);
ASSERT_EVENTS_PrmIdUpdated_SIZE(0);
// 1.4 Attempt PRM_COMMIT_STAGED during loading
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_COMMIT_STAGED(0, 12);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_COMMIT_STAGED, 12, Fw::CmdResponse::VALIDATION_ERROR);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::LOADING_FILE_UPDATES,
PrmDb_PrmLoadAction::COMMIT_STAGED_COMMAND);
// Verify state hasn't changed
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::LOADING_FILE_UPDATES);
// -------------------------------------------------------------------
// 2. Test illegal operations in FILE_UPDATES_STAGED state
// -------------------------------------------------------------------
this->m_impl.m_state = PrmDbFileLoadState::FILE_UPDATES_STAGED;
// 2.1 Attempt PRM_LOAD_FILE when updates are staged
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_LOAD_FILE(0, 13, Fw::String("file.prm"), PrmDb_Merge::RESET);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_LOAD_FILE, 13, Fw::CmdResponse::BUSY);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::FILE_UPDATES_STAGED,
PrmDb_PrmLoadAction::LOAD_FILE_COMMAND);
// 2.2 Attempt PRM_SAVE_FILE when updates are staged
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_SAVE_FILE(0, 14);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 14, Fw::CmdResponse::BUSY);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::FILE_UPDATES_STAGED,
PrmDb_PrmLoadAction::SAVE_FILE_COMMAND);
// 2.3 Attempt to set parameter when updates are staged
this->clearEvents();
this->clearHistory();
this->invoke_to_setPrm(0, testId, pBuff);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response (warning event only, no added event)
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::FILE_UPDATES_STAGED,
PrmDb_PrmLoadAction::SET_PARAMETER);
ASSERT_EVENTS_PrmIdAdded_SIZE(0);
ASSERT_EVENTS_PrmIdUpdated_SIZE(0);
// Verify state hasn't changed
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::FILE_UPDATES_STAGED);
// -------------------------------------------------------------------
// 3. Test illegal operations in IDLE state
// -------------------------------------------------------------------
this->m_impl.m_state = PrmDbFileLoadState::IDLE;
// 3.1 Attempt PRM_COMMIT_STAGED in IDLE state
this->clearEvents();
this->clearHistory();
this->sendCmd_PRM_COMMIT_STAGED(0, 15);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify appropriate error response
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_COMMIT_STAGED, 15, Fw::CmdResponse::VALIDATION_ERROR);
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction_SIZE(1);
ASSERT_EVENTS_PrmDbFileLoadInvalidAction(0, PrmDbFileLoadState::IDLE, PrmDb_PrmLoadAction::COMMIT_STAGED_COMMAND);
// Verify state hasn't changed
EXPECT_EQ(this->m_impl.m_state, PrmDbFileLoadState::IDLE);
// 3.2 Verify legal operations are still allowed in IDLE state
// Setting a parameter should work in IDLE state
this->clearEvents();
this->clearHistory();
this->invoke_to_setPrm(0, testId, pBuff);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify parameter was added
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
// -------------------------------------------------------------------
// 4. Test state transitions back to IDLE after errors
// -------------------------------------------------------------------
// 4.1 Test that failed file load sets state back to IDLE
this->m_impl.m_state = PrmDbFileLoadState::LOADING_FILE_UPDATES;
this->m_impl.clearDb(PrmDbType::DB_STAGING);
// Simulate failed file load
this->m_impl.m_state = PrmDbFileLoadState::IDLE;
// Verify that setPrm now works
this->clearEvents();
this->clearHistory();
this->invoke_to_setPrm(0, testId + 1, pBuff);
dispatchStatus = this->m_impl.doDispatch();
EXPECT_EQ(dispatchStatus, Fw::QueuedComponentBase::MSG_DISPATCH_OK);
// Verify parameter was added
ASSERT_EVENTS_SIZE(1);
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
}
PrmDbTester* PrmDbTester::PrmDbTestFile::s_tester = nullptr;
void PrmDbTester::PrmDbTestFile::setTester(Svc::PrmDbTester* tester) {
ASSERT_NE(tester, nullptr);
s_tester = tester;
}
Os::File::Status PrmDbTester::PrmDbTestFile::read(U8* buffer, FwSizeType& size, Os::File::WaitType wait) {
EXPECT_NE(s_tester, nullptr);
Os::File::Status status = this->Os::Stub::File::Test::TestFile::read(buffer, size, wait);
if (s_tester->m_waits == 0) {
switch (s_tester->m_errorType) {
case FILE_STATUS_ERROR:
status = s_tester->m_status;
break;
case FILE_SIZE_ERROR:
size += 1;
break;
case FILE_DATA_ERROR:
buffer[0] += 1;
break;
default:
break;
}
} else {
s_tester->m_waits -= 1;
}
return status;
}
Os::File::Status PrmDbTester::PrmDbTestFile::write(const U8* buffer, FwSizeType& size, Os::File::WaitType wait) {
EXPECT_NE(s_tester, nullptr);
Os::File::Status status = this->Os::Stub::File::Test::TestFile::write(buffer, size, wait);
if (s_tester->m_waits == 0) {
switch (s_tester->m_errorType) {
case FILE_STATUS_ERROR:
status = s_tester->m_status;
break;
case FILE_SIZE_ERROR:
size += 1;
break;
default:
break;
}
} else {
s_tester->m_waits -= 1;
}
return status;
}
PrmDbTester::PrmDbTester(Svc::PrmDbImpl& inst) : PrmDbGTestBase("testerbase", 100), m_impl(inst) {
PrmDbTester::PrmDbTestFile::setTester(this);
}
PrmDbTester::~PrmDbTester() {}
void PrmDbTester ::from_pingOut_handler(const FwIndexType portNum, U32 key) {
this->pushFromPortEntry_pingOut(key);
}
void PrmDbTester::printDb(PrmDb_PrmDbType dbType) {
PrmDbImpl::t_dbStruct* db = this->m_impl.getDbPtr(dbType);
printf("%s Parameter DB @ %p \n", PrmDbImpl::getDbString(dbType).toChar(), static_cast<void*>(db));
for (FwSizeType entry = 0; entry < PRMDB_NUM_DB_ENTRIES; entry++) {
U8* data = db[entry].val.getBuffAddr();
FwSizeType len = db[entry].val.getBuffLength();
if (db[entry].used) {
std::cout << " " << std::setw(2) << entry << " :";
printf(" ID = %08X", db[entry].id);
printf(" Value = ");
for (FwSizeType i = 0; i < len; ++i) {
printf("%02X ", data[i]);
}
printf("\n");
}
}
}
} /* namespace Svc */
namespace Os {
//! \brief get a delegate for FileInterface that intercepts calls for parameter database testing
//! \param aligned_new_memory: aligned memory to fill
//! \param to_copy: pointer to copy-constructor input
//! \return: pointer to delegate
FileInterface* FileInterface::getDelegate(FileHandleStorage& aligned_placement_new_memory,
const FileInterface* to_copy) {
return Os::Delegate::makeDelegate<FileInterface, Svc::PrmDbTester::PrmDbTestFile>(aligned_placement_new_memory,
to_copy);
}
//! \brief get a delegate for FileSystemInterface that intercepts calls for stub fileSystem usage
//! \param aligned_new_memory: aligned memory to fill
//! \param to_copy: pointer to copy-constructor input
//! \return: pointer to delegate
FileSystemInterface* FileSystemInterface::getDelegate(FileSystemHandleStorage& aligned_placement_new_memory) {
return Os::Delegate::makeDelegate<FileSystemInterface, Os::Stub::FileSystem::StubFileSystem>(
aligned_placement_new_memory);
}
//! \brief get a delegate for DirectoryInterface that intercepts calls for stub Directory usage
//! \param aligned_new_memory: aligned memory to fill
//! \return: pointer to delegate
DirectoryInterface* DirectoryInterface::getDelegate(DirectoryHandleStorage& aligned_placement_new_memory) {
return Os::Delegate::makeDelegate<DirectoryInterface, Os::Stub::Directory::StubDirectory>(
aligned_placement_new_memory);
}
} // namespace Os