fprime/Svc/TlmChan/test/ut/TlmChanTester.cpp
M Starch cddf38bb6f
Make Os::Queues use Fw::MemAllocator pattern for memory (#4451)
* Queues use MemAllocator pattern

* Derive queue allocation from MallocRegistry

* Formatting

* Fix UTs

* Fix CI

* Fix alignment in UT

* Formatting and sp

* Formatting, bad header

* More formatting

* Add queue teardown

* Deinit components

* Fix priority queue test

* Fix bug in priority queue allocation

* Correct comments

* Fix FppTest and Ref UTs

* Fix max heap teardown

* Fix review comment on max heap

* Fix null -> nullptr
2025-12-02 17:36:15 -08:00

351 lines
11 KiB
C++

// ======================================================================
// \title TlmChan.hpp
// \author tcanham
// \brief cpp file for TlmChan test harness implementation class
// ======================================================================
#include "TlmChanTester.hpp"
#include <Fw/Test/UnitTest.hpp>
#define INSTANCE 0
#define MAX_HISTORY_SIZE 10
#define QUEUE_DEPTH 10
static const FwChanIdType TEST_CHAN_SIZE = sizeof(FwChanIdType) + Fw::Time::SERIALIZED_SIZE + sizeof(U32);
static const FwChanIdType CHANS_PER_COMBUFFER =
(FW_COM_BUFFER_MAX_SIZE - sizeof(FwPacketDescriptorType)) / TEST_CHAN_SIZE;
static constexpr FwSizeType INTEGER_DIVISION_ROUNDED_UP(FwSizeType a, FwSizeType b) {
return ((a % b) == 0) ? (a / b) : (a / b) + 1;
}
namespace Svc {
// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------
TlmChanTester ::TlmChanTester()
: TlmChanGTestBase("Tester", MAX_HISTORY_SIZE), component("TlmChan"), m_numBuffs(0), m_bufferRecv(false) {
this->initComponents();
this->connectPorts();
}
TlmChanTester ::~TlmChanTester() {
this->component.deinit();
}
// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------
void TlmChanTester::runNominalChannel() {
this->clearBuffs();
// send first buffer
this->sendBuff(27, 10);
this->doRun(true);
this->checkBuff(0, 1, 27, 10);
this->clearBuffs();
// send again to other buffer
this->sendBuff(27, 10);
static bool tlc003 = false;
if (not tlc003) {
REQUIREMENT("TLC-003");
tlc003 = true;
}
this->doRun(true);
this->checkBuff(0, 1, 27, 10);
// do an update to make sure it gets updated and returned correctly
this->clearBuffs();
this->sendBuff(27, 20);
}
void TlmChanTester::runMultiChannel() {
FwChanIdType ID_0[] = {// Test channel IDs
0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1100, 0x1101, 0x1102, 0x1103, 0x300,
0x301, 0x400, 0x401, 0x402, 0x100, 0x101, 0x102, 0x103, 0x104, 0x105};
this->clearBuffs();
// send all updates
for (FwChanIdType n = 0; n < FW_NUM_ARRAY_ELEMENTS(ID_0); n++) {
this->sendBuff(ID_0[n], static_cast<U32>(n));
}
ASSERT_EQ(0, this->component.m_activeBuffer);
// do a run, and all the packets should be sent
this->doRun(true);
ASSERT_TRUE(this->m_bufferRecv);
ASSERT_EQ(INTEGER_DIVISION_ROUNDED_UP(FW_NUM_ARRAY_ELEMENTS(ID_0), CHANS_PER_COMBUFFER), this->m_numBuffs);
ASSERT_EQ(1, this->component.m_activeBuffer);
// verify packets
for (FwChanIdType n = 0; n < FW_NUM_ARRAY_ELEMENTS(ID_0); n++) {
// printf("#: %d\n",n);
this->checkBuff(n, FW_NUM_ARRAY_ELEMENTS(ID_0), ID_0[n], static_cast<U32>(n));
}
// send another set
FwChanIdType ID_1[] = {// Test channel IDs
0x5000, 0x5001, 0x5002, 0x5003, 0x5004, 0x5005, 0x5100, 0x5101, 0x5102,
0x5103, 0x6300, 0x6301, 0x6400, 0x6401, 0x6402, 0x6100, 0x6101, 0x6102,
0x6103, 0x6104, 0x6105, 0x8101, 0x8102, 0x8103, 0x8104, 0x8105};
this->clearBuffs();
// send all updates
for (FwChanIdType n = 0; n < FW_NUM_ARRAY_ELEMENTS(ID_1); n++) {
this->sendBuff(ID_1[n], static_cast<U32>(n));
}
ASSERT_EQ(1, this->component.m_activeBuffer);
// do a run, and all the packets should be sent
this->doRun(true);
ASSERT_TRUE(this->m_bufferRecv);
ASSERT_EQ(INTEGER_DIVISION_ROUNDED_UP(FW_NUM_ARRAY_ELEMENTS(ID_1), CHANS_PER_COMBUFFER), this->m_numBuffs);
ASSERT_EQ(0, this->component.m_activeBuffer);
// verify packets
for (FwChanIdType n = 0; n < FW_NUM_ARRAY_ELEMENTS(ID_1); n++) {
// printf("#: %d\n",n);
this->checkBuff(n, FW_NUM_ARRAY_ELEMENTS(ID_1), ID_1[n], static_cast<U32>(n));
}
}
void TlmChanTester::runOffNominal() {
// Ask for a packet that isn't written yet
Fw::TlmBuffer buff;
Fw::SerializeStatus stat;
Fw::Time timeTag;
U32 val = 10;
// create Telemetry item and put dummy data in to make sure it gets erased
buff.resetSer();
stat = buff.serializeFrom(val);
ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat);
// Read back value
Fw::TlmValid valid = this->invoke_to_TlmGet(0, 10, timeTag, buff);
ASSERT_EQ(0u, buff.getSize());
ASSERT_EQ(valid, Fw::TlmValid::INVALID);
}
// ----------------------------------------------------------------------
// Handlers for typed from ports
// ----------------------------------------------------------------------
void TlmChanTester ::from_PktSend_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
this->pushFromPortEntry_PktSend(data, context);
this->m_bufferRecv = true;
this->m_rcvdBuffer[this->m_numBuffs] = data;
this->m_numBuffs++;
}
void TlmChanTester ::from_pingOut_handler(const FwIndexType portNum, U32 key) {
this->pushFromPortEntry_pingOut(key);
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
bool TlmChanTester::doRun(bool check) {
// execute run port to send packet
this->invoke_to_Run(0, 0);
// dispatch run message
this->m_bufferRecv = false;
this->component.doDispatch();
if (check) {
EXPECT_TRUE(this->m_bufferRecv);
}
return this->m_bufferRecv;
}
void TlmChanTester::checkBuff(FwChanIdType chanNum, FwChanIdType totalChan, FwChanIdType id, U32 val) {
Fw::Time timeTag;
// deserialize packet
Fw::SerializeStatus stat;
static bool tlc004 = false;
if (not tlc004) {
REQUIREMENT("TLC-004");
tlc004 = true;
}
FwChanIdType currentChan = 0;
// Search for channel ID
for (FwChanIdType packet = 0; packet < this->m_numBuffs; packet++) {
// Look at packet descriptor for current packet
this->m_rcvdBuffer[packet].resetDeser();
// first piece should be tlm packet descriptor
FwPacketDescriptorType desc;
stat = this->m_rcvdBuffer[packet].deserializeTo(desc);
ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat);
ASSERT_EQ(desc, static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_TELEM));
for (FwChanIdType chan = 0; chan < CHANS_PER_COMBUFFER; chan++) {
// decode channel ID
FwEventIdType sentId;
stat = this->m_rcvdBuffer[packet].deserializeTo(sentId);
ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat);
// next piece is time tag
Fw::Time recTimeTag(TimeBase::TB_NONE, 0, 0);
stat = this->m_rcvdBuffer[packet].deserializeTo(recTimeTag);
ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat);
ASSERT_TRUE(timeTag == recTimeTag);
// next piece is event argument
U32 readVal;
stat = this->m_rcvdBuffer[packet].deserializeTo(readVal);
ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat);
if (chanNum == currentChan) {
ASSERT_EQ(id, sentId);
ASSERT_EQ(val, readVal);
}
// quit if we are at max channel entry
if (currentChan == (totalChan - 1)) {
break;
}
currentChan++;
}
// packet should be empty
ASSERT_EQ(0, this->m_rcvdBuffer[packet].getDeserializeSizeLeft());
}
}
void TlmChanTester::sendBuff(FwChanIdType id, U32 val) {
Fw::TlmBuffer buff;
Fw::TlmBuffer readBack;
Fw::SerializeStatus stat;
Fw::Time timeTag;
U32 retestVal;
// create telemetry item
buff.resetSer();
stat = buff.serializeFrom(val);
ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat);
static bool tlc001 = false;
if (not tlc001) {
REQUIREMENT("TLC-001");
tlc001 = true;
}
this->invoke_to_TlmRecv(0, id, timeTag, buff);
// Read back value
static bool tlc002 = false;
if (not tlc002) {
REQUIREMENT("TLC-002");
tlc002 = true;
}
Fw::TlmValid valid = this->invoke_to_TlmGet(0, id, timeTag, readBack);
// deserialize value
retestVal = 0;
readBack.deserializeTo(retestVal);
ASSERT_EQ(retestVal, val);
ASSERT_EQ(valid, Fw::TlmValid::VALID);
}
void TlmChanTester::clearBuffs() {
this->m_numBuffs = 0;
for (FwChanIdType n = 0; n < TLMCHAN_HASH_BUCKETS; n++) {
this->m_rcvdBuffer[n].resetSer();
}
}
void TlmChanTester::dumpTlmEntry(TlmChan::TlmEntry* entry) {
printf(
"Entry "
" Ptr: %p"
" id: 0x%" PRI_FwChanIdType " bucket: %" PRI_FwChanIdType " next: %p\n",
static_cast<void*>(entry), entry->id, entry->bucketNo, static_cast<void*>(entry->next));
}
void TlmChanTester::dumpHash() {
// printf("**Buffer 0\n");
for (FwChanIdType slot = 0; slot < TLMCHAN_NUM_TLM_HASH_SLOTS; slot++) {
printf("Slot: %" PRI_FwChanIdType "\n", slot);
if (this->component.m_tlmEntries[0].slots[slot]) {
TlmChan::TlmEntry* entry = component.m_tlmEntries[0].slots[slot];
for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS; bucket++) {
dumpTlmEntry(entry);
if (entry->next == nullptr) {
break;
} else {
entry = entry->next;
}
}
} else {
printf("EMPTY\n");
}
}
printf("\n");
// for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS; bucket++) {
// printf("Bucket: %d ",bucket);
// dumpTlmEntry(&m_impl.m_tlmEntries[0].buckets[bucket]);
// }
// printf("**Buffer 1\n");
// for (FwChanIdType slot = 0; slot < TLMCHAN_NUM_TLM_HASH_SLOTS; slot++) {
// printf("Slot: %d\n",slot);
// if (m_impl.m_tlmEntries[1].slots[slot]) {
// TlmChanImpl::TlmEntry* entry = m_impl.m_tlmEntries[1].slots[slot];
// for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS; bucket++) {
// dumpTlmEntry(entry);
// if (entry->next == 0) {
// break;
// } else {
// entry = entry->next;
// }
// }
// } else {
// printf("EMPTY\n");
// }
// }
// printf("\n");
// for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS; bucket++) {
// printf("Bucket: %d\n",bucket);
// dumpTlmEntry(&m_impl.m_tlmEntries[1].buckets[bucket]);
// }
}
void TlmChanTester ::connectPorts() {
// Run
this->connect_to_Run(0, this->component.get_Run_InputPort(0));
// TlmGet
this->connect_to_TlmGet(0, this->component.get_TlmGet_InputPort(0));
// TlmRecv
this->connect_to_TlmRecv(0, this->component.get_TlmRecv_InputPort(0));
// pingIn
this->connect_to_pingIn(0, this->component.get_pingIn_InputPort(0));
// PktSend
this->component.set_PktSend_OutputPort(0, this->get_from_PktSend(0));
// pingOut
this->component.set_pingOut_OutputPort(0, this->get_from_pingOut(0));
}
void TlmChanTester ::initComponents() {
this->init();
this->component.init(QUEUE_DEPTH, INSTANCE);
}
} // end namespace Svc