Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Svc/Ccsds/ApidManager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ register_fprime_ut(
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/test/ut/ApidManagerTestMain.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/ApidManagerTester.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/TestState/TestState.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/Rules/GetSeqCount.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/Rules/ValidateSeqCount.cpp"
AUTOCODER_INPUTS
"${CMAKE_CURRENT_LIST_DIR}/ApidManager.fpp"
DEPENDS
Expand Down
71 changes: 42 additions & 29 deletions Svc/Ccsds/ApidManager/test/ut/ApidManagerTestMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,64 @@
// \brief cpp file for ApidManager component test main function
// ======================================================================

#include "ApidManagerTester.hpp"
#include "STest/Random/Random.hpp"
#include "STest/Scenario/BoundedScenario.hpp"
#include "STest/Scenario/RandomScenario.hpp"
#include "Svc/Ccsds/ApidManager/test/ut/ApidManagerTester.hpp"

namespace Svc {

namespace Ccsds {

// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------

// Verify that getApidSeqCountIn registers a new APID and returns
// incrementing counts on subsequent calls.
TEST(ApidManager, GetSequenceCounts) {
Svc::Ccsds::ApidManagerTester tester;
Svc::Ccsds::ApidManagerTester::GetExistingSeqCount getExistingSeqCount;
Svc::Ccsds::ApidManagerTester::GetNewSeqCountOk getNewSeqCountOk;
getExistingSeqCount.apply(tester);
getNewSeqCountOk.apply(tester);
ApidManagerTester tester;
ApidManagerTester::GetSeqCount__NewOk ruleNewOk;
ApidManagerTester::GetSeqCount__Existing ruleExisting;
ruleNewOk.apply(tester); // register a new APID; expect count 0
ruleExisting.apply(tester); // retrieve count for the same APID; expect count 1
}

// Verify that validateApidSeqCountIn fires no event on a matching count
// and fires UnexpectedSequenceCount on a mismatch.
TEST(ApidManager, ValidateSequenceCounts) {
Svc::Ccsds::ApidManagerTester tester;
Svc::Ccsds::ApidManagerTester::ValidateSeqCountOk validateSeqCountOkRule;
Svc::Ccsds::ApidManagerTester::ValidateSeqCountFailure validateSeqCountFailureRule;
validateSeqCountOkRule.apply(tester);
validateSeqCountFailureRule.apply(tester);
ApidManagerTester tester;
ApidManagerTester::GetSeqCount__NewOk ruleNewOk;
ApidManagerTester::ValidateSeqCount__Ok ruleValidateOk;
ApidManagerTester::ValidateSeqCount__Failure ruleValidateFailure;
ruleNewOk.apply(tester); // register an APID so validate rules can fire
ruleValidateOk.apply(tester); // validate correct count; no event expected
ruleValidateFailure.apply(tester); // validate wrong count; event expected
}

// Randomized testing
// Randomized test: apply rules in a random sequence for a large number of iterations
TEST(ApidManager, RandomizedTesting) {
Svc::Ccsds::ApidManagerTester tester;
FwSizeType numRulesToApply = 10000;
ApidManagerTester tester;
ApidManagerTester::GetSeqCount__Existing ruleGetExisting;
ApidManagerTester::GetSeqCount__NewOk ruleGetNewOk;
ApidManagerTester::GetSeqCount__NewTableFull ruleGetNewTableFull;
ApidManagerTester::ValidateSeqCount__Ok ruleValidateOk;
ApidManagerTester::ValidateSeqCount__Failure ruleValidateFailure;

Svc::Ccsds::ApidManagerTester::GetExistingSeqCount getExistingSeqCountRule;
Svc::Ccsds::ApidManagerTester::GetNewSeqCountOk getNewSeqCountOkRule;
Svc::Ccsds::ApidManagerTester::GetNewSeqCountTableFull getNewSeqCountTableFullRule;
Svc::Ccsds::ApidManagerTester::ValidateSeqCountOk validateSeqCountOkRule;
Svc::Ccsds::ApidManagerTester::ValidateSeqCountFailure validateSeqCountFailureRule;
STest::Rule<ApidManagerTester>* rules[] = {
&ruleGetExisting, &ruleGetNewOk, &ruleGetNewTableFull, &ruleValidateOk, &ruleValidateFailure,
};

// Place these rules into a list of rules
STest::Rule<Svc::Ccsds::ApidManagerTester>* rules[] = {&getExistingSeqCountRule, &getNewSeqCountOkRule,
&getNewSeqCountTableFullRule, &validateSeqCountOkRule,
&validateSeqCountFailureRule};

// Take the rules and place them into a random scenario
STest::RandomScenario<Svc::Ccsds::ApidManagerTester> random("Random Rules", rules, FW_NUM_ARRAY_ELEMENTS(rules));

// Create a bounded scenario wrapping the random scenario
STest::BoundedScenario<Svc::Ccsds::ApidManagerTester> bounded("Bounded Random Rules Scenario", random, 10000);
// Run!
STest::RandomScenario<ApidManagerTester> random("Random Rules", rules, FW_NUM_ARRAY_ELEMENTS(rules));
STest::BoundedScenario<ApidManagerTester> bounded("Bounded Random Rules Scenario", random, numRulesToApply);
const U32 numSteps = bounded.run(tester);
printf("Ran %u steps.\n", numSteps);
}

} // namespace Ccsds
} // namespace Svc

int main(int argc, char** argv) {
STest::Random::seed();
::testing::InitGoogleTest(&argc, argv);
Expand Down
155 changes: 3 additions & 152 deletions Svc/Ccsds/ApidManager/test/ut/ApidManagerTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,172 +5,23 @@
// ======================================================================

#include "ApidManagerTester.hpp"
#include "STest/Random/Random.hpp"
#include "Svc/Ccsds/Types/FppConstantsAc.hpp"

namespace Svc {

namespace Ccsds {

static constexpr ComCfg::Apid::T TEST_REGISTERED_APIDS[] = {ComCfg::Apid::FW_PACKET_COMMAND,
ComCfg::Apid::FW_PACKET_TELEM, ComCfg::Apid::FW_PACKET_LOG,
ComCfg::Apid::FW_PACKET_FILE};

// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------

ApidManagerTester ::ApidManagerTester()
ApidManagerTester::ApidManagerTester()
: ApidManagerGTestBase("ApidManagerTester", ApidManagerTester::MAX_HISTORY_SIZE), component("ApidManager") {
this->initComponents();
this->connectPorts();
// Initialize existing sequence counts for common APIDs
for (FwIndexType i = 0; i < static_cast<FwIndexType>(FW_NUM_ARRAY_ELEMENTS(TEST_REGISTERED_APIDS)); i++) {
this->component.m_apidSequences.insert(TEST_REGISTERED_APIDS[i], static_cast<U16>(0));
this->shadow_seqCounts[TEST_REGISTERED_APIDS[i]] = 0; // Initialize shadow sequence counts to 0
}
}

ApidManagerTester ::~ApidManagerTester() {}

// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------

bool ApidManagerTester::GetExistingSeqCount::precondition(const ApidManagerTester& testerState) {
return true; // Can always get existing sequence count
}

void ApidManagerTester::GetExistingSeqCount::action(ApidManagerTester& testerState) {
testerState.clearHistory();
ComCfg::Apid::T apid = testerState.shadow_getRandomTrackedApid();
U16 seqCount = testerState.invoke_to_getApidSeqCountIn(0, apid, 0);
U16 shadowSeqCount = testerState.shadow_getAndIncrementSeqCount(apid);
ASSERT_EQ(seqCount, shadowSeqCount) << "Sequence count for APID " << static_cast<U16>(apid)
<< " does not match shadow value."
<< " Shadow: " << shadowSeqCount << ", Actual: " << seqCount;
}

bool ApidManagerTester::GetNewSeqCountOk::precondition(const ApidManagerTester& testerState) {
return testerState.shadow_isTableFull == false;
}

void ApidManagerTester::GetNewSeqCountOk::action(ApidManagerTester& testerState) {
testerState.clearHistory();
// Use local constexpr to potentially avoid ODR-use of ApidManager::MAX_TRACKED_APIDS
constexpr U8 maxTrackedApidsVal = ApidManager::MAX_TRACKED_APIDS;
bool isTableFull = !(testerState.shadow_seqCounts.size() < maxTrackedApidsVal);
if (isTableFull) {
testerState.shadow_isTableFull = true;
return; // Cannot get new sequence count if table is full - skip action
}

ComCfg::Apid::T apid = testerState.shadow_getRandomUntrackedApid();
U16 seqCount = testerState.invoke_to_getApidSeqCountIn(0, apid, 0);
U16 shadowSeqCount = testerState.shadow_getAndIncrementSeqCount(apid);
ASSERT_EQ(seqCount, shadowSeqCount) << "Sequence count for APID " << static_cast<U16>(apid)
<< " does not match shadow value."
<< " Shadow: " << shadowSeqCount << ", Actual: " << seqCount;
}

bool ApidManagerTester::GetNewSeqCountTableFull::precondition(const ApidManagerTester& testerState) {
return testerState.shadow_isTableFull == true;
}

void ApidManagerTester::GetNewSeqCountTableFull::action(ApidManagerTester& testerState) {
testerState.clearHistory();
ComCfg::Apid::T apid = testerState.shadow_getRandomUntrackedApid();
U16 seqCount = testerState.invoke_to_getApidSeqCountIn(0, apid, 0);
// Use local constexpr to potentially avoid ODR-use of ApidManager::SEQUENCE_COUNT_ERROR
constexpr U16 sequenceCountErrorVal = ApidManager::SEQUENCE_COUNT_ERROR;
ASSERT_EQ(seqCount, sequenceCountErrorVal)
<< "Expected SEQUENCE_COUNT_ERROR for untracked APID " << static_cast<U16>(apid) << ", but got " << seqCount;
testerState.assertEvents_ApidTableFull_size(__FILE__, __LINE__, 1);
testerState.assertEvents_ApidTableFull(__FILE__, __LINE__, 0, static_cast<U16>(apid));
}

bool ApidManagerTester::ValidateSeqCountOk::precondition(const ApidManagerTester& testerState) {
return true;
}

void ApidManagerTester::ValidateSeqCountOk::action(ApidManagerTester& testerState) {
testerState.clearHistory();
ComCfg::Apid::T apid = testerState.shadow_getRandomTrackedApid();
U16 shadow_expectedSeqCount = testerState.shadow_seqCounts[apid];
testerState.invoke_to_validateApidSeqCountIn(0, apid, shadow_expectedSeqCount);
testerState.shadow_validateApidSeqCount(apid, shadow_expectedSeqCount); // keep shadow state in sync

testerState.assertEvents_UnexpectedSequenceCount_size(__FILE__, __LINE__, 0);
}

bool ApidManagerTester::ValidateSeqCountFailure::precondition(const ApidManagerTester& testerState) {
return true;
}

void ApidManagerTester::ValidateSeqCountFailure::action(ApidManagerTester& testerState) {
testerState.clearHistory();
ComCfg::Apid::T apid = testerState.shadow_getRandomTrackedApid();
U16 shadow_expectedSeqCount = testerState.shadow_seqCounts.at(apid);
U16 invalidSeqCount = static_cast<U16>(
(shadow_expectedSeqCount + 1) %
(1 << SpacePacketSubfields::SeqCountWidth)); // Or any other value that's different, ensure wrap around

// Invoke the port with the deliberately incorrect sequence count
testerState.invoke_to_validateApidSeqCountIn(0, apid, invalidSeqCount);
testerState.shadow_validateApidSeqCount(apid, invalidSeqCount); // keep shadow state in sync

// Now, the event should be logged
testerState.assertEvents_UnexpectedSequenceCount_size(__FILE__, __LINE__, 1);
testerState.assertEvents_UnexpectedSequenceCount(__FILE__, __LINE__, 0, invalidSeqCount, shadow_expectedSeqCount);
}

// ----------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------

U16 ApidManagerTester::shadow_getAndIncrementSeqCount(ComCfg::Apid::T apid) {
// This is a shadow function to simulate the getAndIncrementSeqCount behavior
// without modifying the actual component state.
auto found = this->shadow_seqCounts.find(apid);
if (found != this->shadow_seqCounts.end()) {
U16 seqCount = found->second;
found->second =
static_cast<U16>((seqCount + 1) % (1 << SpacePacketSubfields::SeqCountWidth)); // Increment for next call
return seqCount; // Return the current sequence count
}
// If APID not found, initialize a new entry
if (this->shadow_seqCounts.size() < this->component.MAX_TRACKED_APIDS) {
U16 seqCount = 0;
this->shadow_seqCounts[apid] = static_cast<U16>(seqCount + 1); // increment for next call
return seqCount; // Return the initialized sequence count
}
return this->component.SEQUENCE_COUNT_ERROR; // Return error if APID not found
}

void ApidManagerTester::shadow_validateApidSeqCount(ComCfg::Apid::T apid, U16 expectedSeqCount) {
// This simply updates the shadow state to the next expected sequence count
auto found = this->shadow_seqCounts.find(apid);
if (found != this->shadow_seqCounts.end()) {
found->second = static_cast<U16>((expectedSeqCount + 1) % (1 << SpacePacketSubfields::SeqCountWidth));
}
}

ComCfg::Apid::T ApidManagerTester::shadow_getRandomTrackedApid() {
// Select a random APID from the sequence counts map
U32 mapSize = static_cast<U32>(this->shadow_seqCounts.size());
U32 randomIndex = STest::Random::lowerUpper(0, mapSize - 1);
ComCfg::Apid apid = std::next(this->shadow_seqCounts.begin(), randomIndex)->first;
return apid;
}

ComCfg::Apid::T ApidManagerTester::shadow_getRandomUntrackedApid() {
// Select a random APID that is not currently tracked
ComCfg::Apid::T apid;
do {
apid = static_cast<ComCfg::Apid::T>(STest::Random::lowerUpper(10, ComCfg::Apid::SPP_IDLE_PACKET));
} while (this->shadow_seqCounts.find(apid) != this->shadow_seqCounts.end());
return apid;
}
ApidManagerTester::~ApidManagerTester() {}

} // namespace Ccsds

} // namespace Svc
66 changes: 16 additions & 50 deletions Svc/Ccsds/ApidManager/test/ut/ApidManagerTester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
#ifndef Svc_Ccsds_ApidManagerTester_HPP
#define Svc_Ccsds_ApidManagerTester_HPP

#include "STest/Random/Random.hpp"
#include "STest/Rule/Rule.hpp"
#include "Svc/Ccsds/ApidManager/ApidManager.hpp"
#include "Svc/Ccsds/ApidManager/ApidManagerGTestBase.hpp"
#include "Svc/Ccsds/ApidManager/test/ut/TestState/TestState.hpp"
#include "TestUtils/RuleBasedTesting.hpp"

namespace Svc {

Expand All @@ -22,10 +22,10 @@ class ApidManagerTester : public ApidManagerGTestBase {
// Constants
// ----------------------------------------------------------------------

// Maximum size of histories storing events, telemetry, and port outputs
//! Maximum size of histories storing events, telemetry, and port outputs
static const FwSizeType MAX_HISTORY_SIZE = 10;

// Instance ID supplied to the component instance under test
//! Instance ID supplied to the component instance under test
static const FwEnumStoreType TEST_INSTANCE_ID = 0;

public:
Expand All @@ -41,7 +41,7 @@ class ApidManagerTester : public ApidManagerGTestBase {

private:
// ----------------------------------------------------------------------
// Helper functions
// Helper functions (auto-generated via UT_AUTO_HELPERS)
// ----------------------------------------------------------------------

//! Connect ports
Expand All @@ -58,56 +58,22 @@ class ApidManagerTester : public ApidManagerGTestBase {
//! The component under test
ApidManager component;

// Shadow test state
std::map<ComCfg::Apid::T, U16> shadow_seqCounts; //!< Map to hold expected sequence counts for APIDs
bool shadow_isTableFull = false;
//! Shadow state for rule-based testing
ApidManagerTestState shadow;

public:
// ----------------------------------------------------------------------
// Helpers for tracking the shadow test state
// Rule Based Testing
// ----------------------------------------------------------------------

U16 shadow_getAndIncrementSeqCount(ComCfg::Apid::T apid);

void shadow_validateApidSeqCount(ComCfg::Apid::T apid, U16 expectedSeqCount);

ComCfg::Apid::T shadow_getRandomTrackedApid();

ComCfg::Apid::T shadow_getRandomUntrackedApid();
//! Rules for the getApidSeqCountIn port
FW_RBT_DEFINE_RULE(ApidManagerTester, GetSeqCount, Existing);
FW_RBT_DEFINE_RULE(ApidManagerTester, GetSeqCount, NewOk);
FW_RBT_DEFINE_RULE(ApidManagerTester, GetSeqCount, NewTableFull);

// ----------------------------------------------------------------------
// Tests: Rule Based Testing
// ----------------------------------------------------------------------

public:
struct GetExistingSeqCount : public STest::Rule<ApidManagerTester> {
GetExistingSeqCount() : STest::Rule<ApidManagerTester>("GetExistingSeqCount") {};
bool precondition(const ApidManagerTester& state);
void action(ApidManagerTester& state);
};

struct GetNewSeqCountOk : public STest::Rule<ApidManagerTester> {
GetNewSeqCountOk() : STest::Rule<ApidManagerTester>("GetNewSeqCountOk") {};
bool precondition(const ApidManagerTester& state);
void action(ApidManagerTester& state);
};

struct GetNewSeqCountTableFull : public STest::Rule<ApidManagerTester> {
GetNewSeqCountTableFull() : STest::Rule<ApidManagerTester>("GetNewSeqCountTableFull") {};
bool precondition(const ApidManagerTester& state);
void action(ApidManagerTester& state);
};

struct ValidateSeqCountOk : public STest::Rule<ApidManagerTester> {
ValidateSeqCountOk() : STest::Rule<ApidManagerTester>("ValidateSeqCountOk") {};
bool precondition(const ApidManagerTester& state);
void action(ApidManagerTester& state);
};

struct ValidateSeqCountFailure : public STest::Rule<ApidManagerTester> {
ValidateSeqCountFailure() : STest::Rule<ApidManagerTester>("ValidateSeqCountFailure") {};
bool precondition(const ApidManagerTester& state);
void action(ApidManagerTester& state);
};
//! Rules for the validateApidSeqCountIn port
FW_RBT_DEFINE_RULE(ApidManagerTester, ValidateSeqCount, Ok);
FW_RBT_DEFINE_RULE(ApidManagerTester, ValidateSeqCount, Failure);
};

} // namespace Ccsds
Expand Down
Loading
Loading