Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
66 changes: 37 additions & 29 deletions Svc/Ccsds/ApidManager/test/ut/ApidManagerTestMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,55 @@
// \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"

using Svc::Ccsds::ApidManagerTester;

// ----------------------------------------------------------------------
// 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;

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;

// Place these rules into a list of rules
STest::Rule<Svc::Ccsds::ApidManagerTester>* rules[] = {&getExistingSeqCountRule, &getNewSeqCountOkRule,
&getNewSeqCountTableFullRule, &validateSeqCountOkRule,
&validateSeqCountFailureRule};
U32 numRulesToApply = 10000;
ApidManagerTester tester;
ApidManagerTester::GetSeqCount__Existing ruleGetExisting;
ApidManagerTester::GetSeqCount__NewOk ruleGetNewOk;
ApidManagerTester::GetSeqCount__NewTableFull ruleGetNewTableFull;
ApidManagerTester::ValidateSeqCount__Ok ruleValidateOk;
ApidManagerTester::ValidateSeqCount__Failure ruleValidateFailure;

// Take the rules and place them into a random scenario
STest::RandomScenario<Svc::Ccsds::ApidManagerTester> random("Random Rules", rules, FW_NUM_ARRAY_ELEMENTS(rules));
STest::Rule<ApidManagerTester>* rules[] = {
&ruleGetExisting, &ruleGetNewOk, &ruleGetNewTableFull, &ruleValidateOk, &ruleValidateFailure,
};

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