1
0
forked from fsfw/fsfw

move HAL and tests folder

This commit is contained in:
2022-07-18 08:59:40 +02:00
parent 3686bbc486
commit 6f7be281ef
229 changed files with 494 additions and 559 deletions

View File

@ -0,0 +1,9 @@
if(FSFW_ADD_INTERNAL_TESTS)
add_subdirectory(internal)
endif()
if(FSFW_BUILD_UNITTESTS)
add_subdirectory(unit)
else()
add_subdirectory(integration)
endif()

View File

@ -0,0 +1,4 @@
add_subdirectory(assemblies)
add_subdirectory(controller)
add_subdirectory(devices)
add_subdirectory(task)

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
TestAssembly.cpp
)

View File

@ -0,0 +1,180 @@
#include "TestAssembly.h"
#include <fsfw/objectmanager/ObjectManager.h>
TestAssembly::TestAssembly(object_id_t objectId, object_id_t parentId, object_id_t testDevice0,
object_id_t testDevice1)
: AssemblyBase(objectId, parentId),
deviceHandler0Id(testDevice0),
deviceHandler1Id(testDevice1) {
ModeListEntry newModeListEntry;
newModeListEntry.setObject(testDevice0);
newModeListEntry.setMode(MODE_OFF);
newModeListEntry.setSubmode(SUBMODE_NONE);
commandTable.insert(newModeListEntry);
newModeListEntry.setObject(testDevice1);
newModeListEntry.setMode(MODE_OFF);
newModeListEntry.setSubmode(SUBMODE_NONE);
commandTable.insert(newModeListEntry);
}
TestAssembly::~TestAssembly() {}
ReturnValue_t TestAssembly::commandChildren(Mode_t mode, Submode_t submode) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestAssembly: Received command to go to mode " << mode << " submode "
<< (int)submode << std::endl;
#else
sif::printInfo("TestAssembly: Received command to go to mode %d submode %d\n", mode, submode);
#endif
ReturnValue_t result = RETURN_OK;
if (mode == MODE_OFF) {
commandTable[0].setMode(MODE_OFF);
commandTable[0].setSubmode(SUBMODE_NONE);
commandTable[1].setMode(MODE_OFF);
commandTable[1].setSubmode(SUBMODE_NONE);
} else if (mode == DeviceHandlerIF::MODE_NORMAL) {
if (submode == submodes::SINGLE) {
commandTable[0].setMode(MODE_OFF);
commandTable[0].setSubmode(SUBMODE_NONE);
commandTable[1].setMode(MODE_OFF);
commandTable[1].setSubmode(SUBMODE_NONE);
// We try to prefer 0 here but we try to switch to 1 even if it might fail
if (isDeviceAvailable(deviceHandler0Id)) {
if (childrenMap[deviceHandler0Id].mode == MODE_ON) {
commandTable[0].setMode(mode);
commandTable[0].setSubmode(SUBMODE_NONE);
} else {
commandTable[0].setMode(MODE_ON);
commandTable[0].setSubmode(SUBMODE_NONE);
result = NEED_SECOND_STEP;
}
} else {
if (childrenMap[deviceHandler1Id].mode == MODE_ON) {
commandTable[1].setMode(mode);
commandTable[1].setSubmode(SUBMODE_NONE);
} else {
commandTable[1].setMode(MODE_ON);
commandTable[1].setSubmode(SUBMODE_NONE);
result = NEED_SECOND_STEP;
}
}
} else {
// Dual Mode Normal
if (childrenMap[deviceHandler0Id].mode == MODE_ON) {
commandTable[0].setMode(mode);
commandTable[0].setSubmode(SUBMODE_NONE);
} else {
commandTable[0].setMode(MODE_ON);
commandTable[0].setSubmode(SUBMODE_NONE);
result = NEED_SECOND_STEP;
}
if (childrenMap[deviceHandler1Id].mode == MODE_ON) {
commandTable[1].setMode(mode);
commandTable[1].setSubmode(SUBMODE_NONE);
} else {
commandTable[1].setMode(MODE_ON);
commandTable[1].setSubmode(SUBMODE_NONE);
result = NEED_SECOND_STEP;
}
}
} else {
// Mode ON
if (submode == submodes::SINGLE) {
commandTable[0].setMode(MODE_OFF);
commandTable[0].setSubmode(SUBMODE_NONE);
commandTable[1].setMode(MODE_OFF);
commandTable[1].setSubmode(SUBMODE_NONE);
// We try to prefer 0 here but we try to switch to 1 even if it might fail
if (isDeviceAvailable(deviceHandler0Id)) {
commandTable[0].setMode(MODE_ON);
commandTable[0].setSubmode(SUBMODE_NONE);
} else {
commandTable[1].setMode(MODE_ON);
commandTable[1].setSubmode(SUBMODE_NONE);
}
} else {
commandTable[0].setMode(MODE_ON);
commandTable[0].setSubmode(SUBMODE_NONE);
commandTable[1].setMode(MODE_ON);
commandTable[1].setSubmode(SUBMODE_NONE);
}
}
HybridIterator<ModeListEntry> iter(commandTable.begin(), commandTable.end());
executeTable(iter);
return result;
}
ReturnValue_t TestAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode) {
switch (mode) {
case MODE_OFF:
if (submode == SUBMODE_NONE) {
return RETURN_OK;
} else {
return INVALID_SUBMODE;
}
case DeviceHandlerIF::MODE_NORMAL:
case MODE_ON:
if (submode < 3) {
return RETURN_OK;
} else {
return INVALID_SUBMODE;
}
}
return INVALID_MODE;
}
ReturnValue_t TestAssembly::initialize() {
ReturnValue_t result = AssemblyBase::initialize();
if (result != RETURN_OK) {
return result;
}
handler0 = ObjectManager::instance()->get<TestDevice>(deviceHandler0Id);
handler1 = ObjectManager::instance()->get<TestDevice>(deviceHandler1Id);
if ((handler0 == nullptr) or (handler1 == nullptr)) {
return HasReturnvaluesIF::RETURN_FAILED;
}
handler0->setParentQueue(this->getCommandQueue());
handler1->setParentQueue(this->getCommandQueue());
result = registerChild(deviceHandler0Id);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
result = registerChild(deviceHandler1Id);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
return result;
}
ReturnValue_t TestAssembly::checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) {
if (submode == submodes::DUAL) {
for (const auto& info : childrenMap) {
if (info.second.mode != wantedMode or info.second.mode != wantedSubmode) {
return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE;
}
}
return RETURN_OK;
} else if (submode == submodes::SINGLE) {
for (const auto& info : childrenMap) {
if (info.second.mode == wantedMode and info.second.mode != wantedSubmode) {
return RETURN_OK;
}
}
}
return INVALID_SUBMODE;
}
bool TestAssembly::isDeviceAvailable(object_id_t object) {
if (healthHelper.healthTable->getHealth(object) == HasHealthIF::HEALTHY) {
return true;
} else {
return false;
}
}

View File

@ -0,0 +1,52 @@
#ifndef MISSION_ASSEMBLIES_TESTASSEMBLY_H_
#define MISSION_ASSEMBLIES_TESTASSEMBLY_H_
#include <fsfw/devicehandlers/AssemblyBase.h>
#include "../devices/TestDeviceHandler.h"
class TestAssembly : public AssemblyBase {
public:
TestAssembly(object_id_t objectId, object_id_t parentId, object_id_t testDevice0,
object_id_t testDevice1);
virtual ~TestAssembly();
ReturnValue_t initialize() override;
enum submodes : Submode_t { SINGLE = 0, DUAL = 1 };
protected:
/**
* Command children to reach [mode,submode] combination
* Can be done by setting #commandsOutstanding correctly,
* or using executeTable()
* @param mode
* @param submode
* @return
* - @c RETURN_OK if ok
* - @c NEED_SECOND_STEP if children need to be commanded again
*/
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
/**
* Check whether desired assembly mode was achieved by checking the modes
* or/and health states of child device handlers.
* The assembly template class will also call this function if a health
* or mode change of a child device handler was detected.
* @param wantedMode
* @param wantedSubmode
* @return
*/
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
private:
FixedArrayList<ModeListEntry, 2> commandTable;
object_id_t deviceHandler0Id = 0;
object_id_t deviceHandler1Id = 0;
TestDevice* handler0 = nullptr;
TestDevice* handler1 = nullptr;
bool isDeviceAvailable(object_id_t object);
};
#endif /* MISSION_ASSEMBLIES_TESTASSEMBLY_H_ */

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
TestController.cpp
)

View File

@ -0,0 +1,37 @@
#include "TestController.h"
#include <fsfw/datapool/PoolReadGuard.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/serviceinterface/ServiceInterface.h>
TestController::TestController(object_id_t objectId, object_id_t parentId, size_t commandQueueDepth)
: ExtendedControllerBase(objectId, parentId, commandQueueDepth) {}
TestController::~TestController() {}
ReturnValue_t TestController::handleCommandMessage(CommandMessage *message) {
return HasReturnvaluesIF::RETURN_OK;
}
void TestController::performControlOperation() {}
void TestController::handleChangedDataset(sid_t sid, store_address_t storeId, bool *clearMessage) {}
void TestController::handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId,
bool *clearMessage) {}
LocalPoolDataSetBase *TestController::getDataSetHandle(sid_t sid) { return nullptr; }
ReturnValue_t TestController::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t TestController::initializeAfterTaskCreation() {
return ExtendedControllerBase::initializeAfterTaskCreation();
}
ReturnValue_t TestController::checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode) {
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -0,0 +1,36 @@
#ifndef MISSION_CONTROLLER_TESTCONTROLLER_H_
#define MISSION_CONTROLLER_TESTCONTROLLER_H_
#include <fsfw/controller/ExtendedControllerBase.h>
#include "../devices/devicedefinitions/testDeviceDefinitions.h"
class TestController : public ExtendedControllerBase {
public:
TestController(object_id_t objectId, object_id_t parentId, size_t commandQueueDepth = 10);
virtual ~TestController();
protected:
// Extended Controller Base overrides
ReturnValue_t handleCommandMessage(CommandMessage* message) override;
void performControlOperation() override;
// HasLocalDatapoolIF callbacks
virtual void handleChangedDataset(sid_t sid, store_address_t storeId,
bool* clearMessage) override;
virtual void handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId,
bool* clearMessage) override;
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) override;
ReturnValue_t initializeAfterTaskCreation() override;
private:
};
#endif /* MISSION_CONTROLLER_TESTCONTROLLER_H_ */

View File

@ -0,0 +1,16 @@
#ifndef MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_
#define MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_
#include <OBSWConfig.h>
#include <fsfw/objectmanager/SystemObjectIF.h>
namespace testcontroller {
enum sourceObjectIds : object_id_t {
DEVICE_0_ID = objects::TEST_DEVICE_HANDLER_0,
DEVICE_1_ID = objects::TEST_DEVICE_HANDLER_1,
};
}
#endif /* MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ */

View File

@ -0,0 +1,5 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
TestCookie.cpp
TestDeviceHandler.cpp
TestEchoComIF.cpp
)

View File

@ -0,0 +1,10 @@
#include "TestCookie.h"
TestCookie::TestCookie(address_t address, size_t replyMaxLen)
: address(address), replyMaxLen(replyMaxLen) {}
TestCookie::~TestCookie() {}
address_t TestCookie::getAddress() const { return address; }
size_t TestCookie::getReplyMaxLen() const { return replyMaxLen; }

View File

@ -0,0 +1,24 @@
#ifndef MISSION_DEVICES_TESTCOOKIE_H_
#define MISSION_DEVICES_TESTCOOKIE_H_
#include <fsfw/devicehandlers/CookieIF.h>
#include <cstddef>
/**
* @brief Really simple cookie which does not do a lot.
*/
class TestCookie : public CookieIF {
public:
TestCookie(address_t address, size_t maxReplyLen);
virtual ~TestCookie();
address_t getAddress() const;
size_t getReplyMaxLen() const;
private:
address_t address = 0;
size_t replyMaxLen = 0;
};
#endif /* MISSION_DEVICES_TESTCOOKIE_H_ */

View File

@ -0,0 +1,793 @@
#include "TestDeviceHandler.h"
#include <cstdlib>
#include "FSFWConfig.h"
#include "fsfw/datapool/PoolReadGuard.h"
TestDevice::TestDevice(object_id_t objectId, object_id_t comIF, CookieIF* cookie,
testdevice::DeviceIndex deviceIdx, bool fullInfoPrintout,
bool changingDataset)
: DeviceHandlerBase(objectId, comIF, cookie),
deviceIdx(deviceIdx),
dataset(this),
fullInfoPrintout(fullInfoPrintout) {}
TestDevice::~TestDevice() = default;
void TestDevice::performOperationHook() {
if (periodicPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx << "::performOperationHook: Alive!" << std::endl;
#else
sif::printInfo("TestDevice%d::performOperationHook: Alive!\n", deviceIdx);
#endif
}
if (oneShot) {
oneShot = false;
}
}
void TestDevice::doStartUp() {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx << "::doStartUp: Switching On" << std::endl;
#else
sif::printInfo("TestDevice%d::doStartUp: Switching On\n", static_cast<int>(deviceIdx));
#endif
}
setMode(_MODE_TO_ON);
return;
}
void TestDevice::doShutDown() {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx << "::doShutDown: Switching Off" << std::endl;
#else
sif::printInfo("TestDevice%d::doShutDown: Switching Off\n", static_cast<int>(deviceIdx));
#endif
}
setMode(_MODE_SHUT_DOWN);
return;
}
ReturnValue_t TestDevice::buildNormalDeviceCommand(DeviceCommandId_t* id) {
using namespace testdevice;
*id = TEST_NORMAL_MODE_CMD;
if (DeviceHandlerBase::isAwaitingReply()) {
return NOTHING_TO_SEND;
}
return buildCommandFromCommand(*id, nullptr, 0);
}
ReturnValue_t TestDevice::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
if (mode == _MODE_TO_ON) {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::buildTransitionDeviceCommand: Was called"
" from _MODE_TO_ON mode"
<< std::endl;
#else
sif::printInfo(
"TestDevice%d::buildTransitionDeviceCommand: "
"Was called from _MODE_TO_ON mode\n",
deviceIdx);
#endif
}
}
if (mode == _MODE_TO_NORMAL) {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::buildTransitionDeviceCommand: Was called "
"from _MODE_TO_NORMAL mode"
<< std::endl;
#else
sif::printInfo(
"TestDevice%d::buildTransitionDeviceCommand: Was called from "
" _MODE_TO_NORMAL mode\n",
deviceIdx);
#endif
}
setMode(MODE_NORMAL);
}
if (mode == _MODE_SHUT_DOWN) {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::buildTransitionDeviceCommand: Was called "
"from _MODE_SHUT_DOWN mode"
<< std::endl;
#else
sif::printInfo(
"TestDevice%d::buildTransitionDeviceCommand: Was called from "
"_MODE_SHUT_DOWN mode\n",
deviceIdx);
#endif
}
setMode(MODE_OFF);
}
return NOTHING_TO_SEND;
}
void TestDevice::doTransition(Mode_t modeFrom, Submode_t submodeFrom) {
if (mode == _MODE_TO_NORMAL) {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::doTransition: Custom transition to "
"normal mode"
<< std::endl;
#else
sif::printInfo("TestDevice%d::doTransition: Custom transition to normal mode\n", deviceIdx);
#endif
}
} else {
DeviceHandlerBase::doTransition(modeFrom, submodeFrom);
}
}
ReturnValue_t TestDevice::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t* commandData,
size_t commandDataLen) {
using namespace testdevice;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
switch (deviceCommand) {
case (TEST_NORMAL_MODE_CMD): {
commandSent = true;
result = buildNormalModeCommand(deviceCommand, commandData, commandDataLen);
break;
}
case (TEST_COMMAND_0): {
commandSent = true;
result = buildTestCommand0(deviceCommand, commandData, commandDataLen);
break;
}
case (TEST_COMMAND_1): {
commandSent = true;
result = buildTestCommand1(deviceCommand, commandData, commandDataLen);
break;
}
case (TEST_NOTIF_SNAPSHOT_VAR): {
if (changingDatasets) {
changingDatasets = false;
}
PoolReadGuard readHelper(&dataset.testUint8Var);
if (deviceIdx == testdevice::DeviceIndex::DEVICE_0) {
/* This will trigger a variable notification to the demo controller */
dataset.testUint8Var = 220;
dataset.testUint8Var.setValid(true);
} else if (deviceIdx == testdevice::DeviceIndex::DEVICE_1) {
/* This will trigger a variable snapshot to the demo controller */
dataset.testUint8Var = 30;
dataset.testUint8Var.setValid(true);
}
break;
}
case (TEST_NOTIF_SNAPSHOT_SET): {
if (changingDatasets) {
changingDatasets = false;
}
PoolReadGuard readHelper(&dataset.testFloat3Vec);
if (deviceIdx == testdevice::DeviceIndex::DEVICE_0) {
/* This will trigger a variable notification to the demo controller */
dataset.testFloat3Vec.value[0] = 60;
dataset.testFloat3Vec.value[1] = 70;
dataset.testFloat3Vec.value[2] = 55;
dataset.testFloat3Vec.setValid(true);
} else if (deviceIdx == testdevice::DeviceIndex::DEVICE_1) {
/* This will trigger a variable notification to the demo controller */
dataset.testFloat3Vec.value[0] = -60;
dataset.testFloat3Vec.value[1] = -70;
dataset.testFloat3Vec.value[2] = -55;
dataset.testFloat3Vec.setValid(true);
}
break;
}
default:
result = DeviceHandlerIF::COMMAND_NOT_SUPPORTED;
}
return result;
}
ReturnValue_t TestDevice::buildNormalModeCommand(DeviceCommandId_t deviceCommand,
const uint8_t* commandData,
size_t commandDataLen) {
if (fullInfoPrintout) {
#if FSFW_VERBOSE_LEVEL >= 3
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice::buildTestCommand1: Building normal command" << std::endl;
#else
sif::printInfo("TestDevice::buildTestCommand1: Building command from TEST_COMMAND_1\n");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* OBSW_VERBOSE_LEVEL >= 3 */
}
if (commandDataLen > MAX_BUFFER_SIZE - sizeof(DeviceCommandId_t)) {
return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS;
}
/* The command is passed on in the command buffer as it is */
passOnCommand(deviceCommand, commandData, commandDataLen);
return RETURN_OK;
}
ReturnValue_t TestDevice::buildTestCommand0(DeviceCommandId_t deviceCommand,
const uint8_t* commandData, size_t commandDataLen) {
using namespace testdevice;
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::buildTestCommand0: Executing simple command "
" with completion reply"
<< std::endl;
#else
sif::printInfo(
"TestDevice%d::buildTestCommand0: Executing simple command with "
"completion reply\n",
deviceIdx);
#endif
}
if (commandDataLen > MAX_BUFFER_SIZE - sizeof(DeviceCommandId_t)) {
return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS;
}
/* The command is passed on in the command buffer as it is */
passOnCommand(deviceCommand, commandData, commandDataLen);
return RETURN_OK;
}
ReturnValue_t TestDevice::buildTestCommand1(DeviceCommandId_t deviceCommand,
const uint8_t* commandData, size_t commandDataLen) {
using namespace testdevice;
if (commandDataLen < 7) {
return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS;
}
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::buildTestCommand1: Executing command with "
"data reply"
<< std::endl;
#else
sif::printInfo("TestDevice%d:buildTestCommand1: Executing command with data reply\n",
deviceIdx);
#endif
}
deviceCommand = EndianConverter::convertBigEndian(deviceCommand);
memcpy(commandBuffer, &deviceCommand, sizeof(deviceCommand));
/* Assign and check parameters */
uint16_t parameter1 = 0;
size_t size = commandDataLen;
ReturnValue_t result =
SerializeAdapter::deSerialize(&parameter1, &commandData, &size, SerializeIF::Endianness::BIG);
if (result == HasReturnvaluesIF::RETURN_FAILED) {
return result;
}
/* Parameter 1 needs to be correct */
if (parameter1 != testdevice::COMMAND_1_PARAM1) {
return DeviceHandlerIF::INVALID_COMMAND_PARAMETER;
}
uint64_t parameter2 = 0;
result =
SerializeAdapter::deSerialize(&parameter2, &commandData, &size, SerializeIF::Endianness::BIG);
if (parameter2 != testdevice::COMMAND_1_PARAM2) {
return DeviceHandlerIF::INVALID_COMMAND_PARAMETER;
}
/* Pass on the parameters to the Echo IF */
commandBuffer[4] = (parameter1 & 0xFF00) >> 8;
commandBuffer[5] = (parameter1 & 0xFF);
parameter2 = EndianConverter::convertBigEndian(parameter2);
memcpy(commandBuffer + 6, &parameter2, sizeof(parameter2));
rawPacket = commandBuffer;
rawPacketLen = sizeof(deviceCommand) + sizeof(parameter1) + sizeof(parameter2);
return RETURN_OK;
}
void TestDevice::passOnCommand(DeviceCommandId_t command, const uint8_t* commandData,
size_t commandDataLen) {
DeviceCommandId_t deviceCommandBe = EndianConverter::convertBigEndian(command);
memcpy(commandBuffer, &deviceCommandBe, sizeof(deviceCommandBe));
memcpy(commandBuffer + 4, commandData, commandDataLen);
rawPacket = commandBuffer;
rawPacketLen = sizeof(deviceCommandBe) + commandDataLen;
}
void TestDevice::fillCommandAndReplyMap() {
namespace td = testdevice;
insertInCommandAndReplyMap(testdevice::TEST_NORMAL_MODE_CMD, 5, &dataset);
insertInCommandAndReplyMap(testdevice::TEST_COMMAND_0, 5);
insertInCommandAndReplyMap(testdevice::TEST_COMMAND_1, 5);
/* No reply expected for these commands */
insertInCommandMap(td::TEST_NOTIF_SNAPSHOT_SET);
insertInCommandMap(td::TEST_NOTIF_SNAPSHOT_VAR);
}
ReturnValue_t TestDevice::scanForReply(const uint8_t* start, size_t len, DeviceCommandId_t* foundId,
size_t* foundLen) {
using namespace testdevice;
/* Unless a command was sent explicitely, we don't expect any replies and ignore this
the packet. On a real device, there might be replies which are sent without a previous
command. */
if (not commandSent) {
return DeviceHandlerBase::IGNORE_FULL_PACKET;
} else {
commandSent = false;
}
if (len < sizeof(object_id_t)) {
return DeviceHandlerIF::LENGTH_MISSMATCH;
}
size_t size = len;
ReturnValue_t result =
SerializeAdapter::deSerialize(foundId, &start, &size, SerializeIF::Endianness::BIG);
if (result != RETURN_OK) {
return result;
}
DeviceCommandId_t pendingCmd = this->getPendingCommand();
switch (pendingCmd) {
case (TEST_NORMAL_MODE_CMD): {
if (fullInfoPrintout) {
#if FSFW_VERBOSE_LEVEL >= 3
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice::scanForReply: Reply for normal commnand (ID "
<< TEST_NORMAL_MODE_CMD << ") received!" << std::endl;
#else
sif::printInfo(
"TestDevice%d::scanForReply: Reply for normal command (ID %d) "
"received!\n",
deviceIdx, TEST_NORMAL_MODE_CMD);
#endif
#endif
}
*foundLen = len;
*foundId = pendingCmd;
return RETURN_OK;
}
case (TEST_COMMAND_0): {
if (len < TEST_COMMAND_0_SIZE) {
return DeviceHandlerIF::LENGTH_MISSMATCH;
}
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::scanForReply: Reply for simple command "
"(ID "
<< TEST_COMMAND_0 << ") received!" << std::endl;
#else
sif::printInfo(
"TestDevice%d::scanForReply: Reply for simple command (ID %d) "
"received!\n",
deviceIdx, TEST_COMMAND_0);
#endif
}
*foundLen = TEST_COMMAND_0_SIZE;
*foundId = pendingCmd;
return RETURN_OK;
}
case (TEST_COMMAND_1): {
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::scanForReply: Reply for data command "
"(ID "
<< TEST_COMMAND_1 << ") received!" << std::endl;
#else
sif::printInfo(
"TestDevice%d::scanForReply: Reply for data command (ID %d) "
"received\n",
deviceIdx, TEST_COMMAND_1);
#endif
}
*foundLen = len;
*foundId = pendingCmd;
return RETURN_OK;
}
default:
return DeviceHandlerIF::DEVICE_REPLY_INVALID;
}
}
ReturnValue_t TestDevice::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
switch (id) {
/* Periodic replies */
case testdevice::TEST_NORMAL_MODE_CMD: {
result = interpretingNormalModeReply();
break;
}
/* Simple reply */
case testdevice::TEST_COMMAND_0: {
result = interpretingTestReply0(id, packet);
break;
}
/* Data reply */
case testdevice::TEST_COMMAND_1: {
result = interpretingTestReply1(id, packet);
break;
}
default:
return DeviceHandlerIF::DEVICE_REPLY_INVALID;
}
return result;
}
ReturnValue_t TestDevice::interpretingNormalModeReply() {
CommandMessage directReplyMessage;
if (changingDatasets) {
PoolReadGuard readHelper(&dataset);
if (dataset.testUint8Var.value == 0) {
dataset.testUint8Var.value = 10;
dataset.testUint32Var.value = 777;
dataset.testFloat3Vec.value[0] = 2.5;
dataset.testFloat3Vec.value[1] = -2.5;
dataset.testFloat3Vec.value[2] = 2.5;
dataset.setValidity(true, true);
} else {
dataset.testUint8Var.value = 0;
dataset.testUint32Var.value = 0;
dataset.testFloat3Vec.value[0] = 0.0;
dataset.testFloat3Vec.value[1] = 0.0;
dataset.testFloat3Vec.value[2] = 0.0;
dataset.setValidity(false, true);
}
return RETURN_OK;
}
PoolReadGuard readHelper(&dataset);
if (dataset.testUint8Var.value == 0) {
/* Reset state */
dataset.testUint8Var.value = 128;
} else if (dataset.testUint8Var.value > 200) {
if (not resetAfterChange) {
/* This will trigger an update notification to the controller */
dataset.testUint8Var.setChanged(true);
resetAfterChange = true;
/* Decrement by 30 automatically. This will prevent any additional notifications. */
dataset.testUint8Var.value -= 30;
}
}
/* If the value is greater than 0, it will be decremented in a linear way */
else if (dataset.testUint8Var.value > 128) {
size_t sizeToDecrement = 0;
if (dataset.testUint8Var.value > 128 + 30) {
sizeToDecrement = 30;
} else {
sizeToDecrement = dataset.testUint8Var.value - 128;
resetAfterChange = false;
}
dataset.testUint8Var.value -= sizeToDecrement;
} else if (dataset.testUint8Var.value < 50) {
if (not resetAfterChange) {
/* This will trigger an update snapshot to the controller */
dataset.testUint8Var.setChanged(true);
resetAfterChange = true;
} else {
/* Increment by 30 automatically. */
dataset.testUint8Var.value += 30;
}
}
/* Increment in linear way */
else if (dataset.testUint8Var.value < 128) {
size_t sizeToIncrement = 0;
if (dataset.testUint8Var.value < 128 - 20) {
sizeToIncrement = 20;
} else {
sizeToIncrement = 128 - dataset.testUint8Var.value;
resetAfterChange = false;
}
dataset.testUint8Var.value += sizeToIncrement;
}
/* TODO: Same for vector */
float vectorMean = (dataset.testFloat3Vec.value[0] + dataset.testFloat3Vec.value[1] +
dataset.testFloat3Vec.value[2]) /
3.0;
/* Lambda (private local function) */
auto sizeToAdd = [](bool tooHigh, float currentVal) {
if (tooHigh) {
if (currentVal - 20.0 > 10.0) {
return -10.0;
} else {
return 20.0 - currentVal;
}
} else {
if (std::abs(currentVal + 20.0) > 10.0) {
return 10.0;
} else {
return -20.0 - currentVal;
}
}
};
if (vectorMean > 20.0 and std::abs(vectorMean - 20.0) > 1.0) {
if (not resetAfterChange) {
dataset.testFloat3Vec.setChanged(true);
resetAfterChange = true;
} else {
float sizeToDecrementVal0 = 0;
float sizeToDecrementVal1 = 0;
float sizeToDecrementVal2 = 0;
sizeToDecrementVal0 = sizeToAdd(true, dataset.testFloat3Vec.value[0]);
sizeToDecrementVal1 = sizeToAdd(true, dataset.testFloat3Vec.value[1]);
sizeToDecrementVal2 = sizeToAdd(true, dataset.testFloat3Vec.value[2]);
dataset.testFloat3Vec.value[0] += sizeToDecrementVal0;
dataset.testFloat3Vec.value[1] += sizeToDecrementVal1;
dataset.testFloat3Vec.value[2] += sizeToDecrementVal2;
}
} else if (vectorMean < -20.0 and std::abs(vectorMean + 20.0) < 1.0) {
if (not resetAfterChange) {
dataset.testFloat3Vec.setChanged(true);
resetAfterChange = true;
} else {
float sizeToDecrementVal0 = 0;
float sizeToDecrementVal1 = 0;
float sizeToDecrementVal2 = 0;
sizeToDecrementVal0 = sizeToAdd(false, dataset.testFloat3Vec.value[0]);
sizeToDecrementVal1 = sizeToAdd(false, dataset.testFloat3Vec.value[1]);
sizeToDecrementVal2 = sizeToAdd(false, dataset.testFloat3Vec.value[2]);
dataset.testFloat3Vec.value[0] += sizeToDecrementVal0;
dataset.testFloat3Vec.value[1] += sizeToDecrementVal1;
dataset.testFloat3Vec.value[2] += sizeToDecrementVal2;
}
} else {
if (resetAfterChange) {
resetAfterChange = false;
}
}
return RETURN_OK;
}
ReturnValue_t TestDevice::interpretingTestReply0(DeviceCommandId_t id, const uint8_t* packet) {
CommandMessage commandMessage;
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice::interpretingTestReply0: Generating step and finish reply"
<< std::endl;
#else
sif::printInfo("TestDevice::interpretingTestReply0: Generating step and finish reply\n");
#endif
}
MessageQueueId_t commander = getCommanderQueueId(id);
/* Generate one step reply and the finish reply */
actionHelper.step(1, commander, id);
actionHelper.finish(true, commander, id);
return RETURN_OK;
}
ReturnValue_t TestDevice::interpretingTestReply1(DeviceCommandId_t id, const uint8_t* packet) {
CommandMessage directReplyMessage;
if (fullInfoPrintout) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx << "::interpretingReply1: Setting data reply"
<< std::endl;
#else
sif::printInfo("TestDevice%d::interpretingReply1: Setting data reply\n", deviceIdx);
#endif
}
MessageQueueId_t commander = getCommanderQueueId(id);
/* Send reply with data */
ReturnValue_t result =
actionHelper.reportData(commander, id, packet, testdevice::TEST_COMMAND_1_SIZE, false);
if (result != RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TestDevice" << deviceIdx
<< "::interpretingReply1: Sending data "
"reply failed!"
<< std::endl;
#else
sif::printError("TestDevice%d::interpretingReply1: Sending data reply failed!\n", deviceIdx);
#endif
return result;
}
if (result == HasReturnvaluesIF::RETURN_OK) {
/* Finish reply */
actionHelper.finish(true, commander, id);
} else {
/* Finish reply */
actionHelper.finish(false, commander, id, result);
}
return RETURN_OK;
}
uint32_t TestDevice::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 5000; }
void TestDevice::enableFullDebugOutput(bool enable) { this->fullInfoPrintout = enable; }
ReturnValue_t TestDevice::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) {
namespace td = testdevice;
localDataPoolMap.emplace(td::PoolIds::TEST_UINT8_ID, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(td::PoolIds::TEST_UINT32_ID, new PoolEntry<uint32_t>({0}));
localDataPoolMap.emplace(td::PoolIds::TEST_FLOAT_VEC_3_ID, new PoolEntry<float>({0.0, 0.0, 0.0}));
sid_t sid(this->getObjectId(), td::TEST_SET_ID);
/* Subscribe for periodic HK packets but do not enable reporting for now.
Non-diangostic with a period of one second */
poolManager.subscribeForPeriodicPacket(sid, false, 1.0, false);
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t TestDevice::getParameter(uint8_t domainId, uint8_t uniqueId,
ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues, uint16_t startAtIndex) {
using namespace testdevice;
switch (uniqueId) {
case ParameterUniqueIds::TEST_UINT32_0: {
if (fullInfoPrintout) {
uint32_t newValue = 0;
ReturnValue_t result = newValues->getElement<uint32_t>(&newValue, 0, 0);
if (result == HasReturnvaluesIF::RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::getParameter: Setting parameter 0 to "
"new value "
<< newValue << std::endl;
#else
sif::printInfo("TestDevice%d::getParameter: Setting parameter 0 to new value %lu\n",
deviceIdx, static_cast<unsigned long>(newValue));
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
}
parameterWrapper->set(testParameter0);
break;
}
case ParameterUniqueIds::TEST_INT32_1: {
if (fullInfoPrintout) {
int32_t newValue = 0;
ReturnValue_t result = newValues->getElement<int32_t>(&newValue, 0, 0);
if (result == HasReturnvaluesIF::RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::getParameter: Setting parameter 1 to "
"new value "
<< newValue << std::endl;
#else
sif::printInfo("TestDevice%d::getParameter: Setting parameter 1 to new value %lu\n",
deviceIdx, static_cast<unsigned long>(newValue));
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
}
parameterWrapper->set(testParameter1);
break;
}
case ParameterUniqueIds::TEST_FLOAT_VEC3_2: {
if (fullInfoPrintout) {
float newVector[3];
if (newValues->getElement<float>(newVector, 0, 0) != RETURN_OK or
newValues->getElement<float>(newVector + 1, 0, 1) != RETURN_OK or
newValues->getElement<float>(newVector + 2, 0, 2) != RETURN_OK) {
return HasReturnvaluesIF::RETURN_FAILED;
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx
<< "::getParameter: Setting parameter 3 to "
"(float vector with 3 entries) to new values ["
<< newVector[0] << ", " << newVector[1] << ", " << newVector[2] << "]"
<< std::endl;
#else
sif::printInfo(
"TestDevice%d::getParameter: Setting parameter 3 to new values "
"[%f, %f, %f]\n",
deviceIdx, newVector[0], newVector[1], newVector[2]);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
parameterWrapper->setVector(vectorFloatParams2);
break;
}
case (ParameterUniqueIds::PERIODIC_PRINT_ENABLED): {
if (fullInfoPrintout) {
uint8_t enabled = 0;
ReturnValue_t result = newValues->getElement<uint8_t>(&enabled, 0, 0);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
char const* printout = nullptr;
if (enabled) {
printout = "enabled";
} else {
printout = "disabled";
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx << "::getParameter: Periodic printout " << printout
<< std::endl;
#else
sif::printInfo("TestDevice%d::getParameter: Periodic printout %s", printout);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
parameterWrapper->set(periodicPrintout);
break;
}
case (ParameterUniqueIds::CHANGING_DATASETS): {
uint8_t enabled = 0;
ReturnValue_t result = newValues->getElement<uint8_t>(&enabled, 0, 0);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
if (not enabled) {
PoolReadGuard readHelper(&dataset);
dataset.testUint8Var.value = 0;
dataset.testUint32Var.value = 0;
dataset.testFloat3Vec.value[0] = 0.0;
dataset.testFloat3Vec.value[0] = 0.0;
dataset.testFloat3Vec.value[1] = 0.0;
}
if (fullInfoPrintout) {
char const* printout = nullptr;
if (enabled) {
printout = "enabled";
} else {
printout = "disabled";
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TestDevice" << deviceIdx << "::getParameter: Changing datasets " << printout
<< std::endl;
#else
sif::printInfo("TestDevice%d::getParameter: Changing datasets %s", printout);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
parameterWrapper->set(changingDatasets);
break;
}
default:
return INVALID_IDENTIFIER_ID;
}
return HasReturnvaluesIF::RETURN_OK;
}
LocalPoolObjectBase* TestDevice::getPoolObjectHandle(lp_id_t localPoolId) {
namespace td = testdevice;
if (localPoolId == td::PoolIds::TEST_UINT8_ID) {
return &dataset.testUint8Var;
} else if (localPoolId == td::PoolIds::TEST_UINT32_ID) {
return &dataset.testUint32Var;
} else if (localPoolId == td::PoolIds::TEST_FLOAT_VEC_3_ID) {
return &dataset.testFloat3Vec;
} else {
return nullptr;
}
}

View File

@ -0,0 +1,134 @@
#ifndef TEST_TESTDEVICES_TESTDEVICEHANDLER_H_
#define TEST_TESTDEVICES_TESTDEVICEHANDLER_H_
#include "devicedefinitions/testDeviceDefinitions.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
#include "fsfw/timemanager/Countdown.h"
/**
* @brief Basic dummy device handler to test device commanding without a physical device.
* @details
* This test device handler provided a basic demo for the device handler object.
* It can also be commanded with the following PUS services, using
* the specified object ID of the test device handler.
*
* 1. PUS Service 8 - Functional commanding
* 2. PUS Service 2 - Device access, raw commanding
* 3. PUS Service 20 - Parameter Management
* 4. PUS Service 3 - Housekeeping
* @author R. Mueller
* @ingroup devices
*/
class TestDevice : public DeviceHandlerBase {
public:
/**
* Build the test device in the factory.
* @param objectId This ID will be assigned to the test device handler.
* @param comIF The ID of the Communication IF used by test device handler.
* @param cookie Cookie object used by the test device handler. This is
* also used and passed to the comIF object.
* @param onImmediately This will start a transition to MODE_ON immediately
* so the device handler jumps into #doStartUp. Should only be used
* in development to reduce need of commanding while debugging.
* @param changingDataset
* Will be used later to change the local datasets containeds in the device.
*/
TestDevice(object_id_t objectId, object_id_t comIF, CookieIF* cookie,
testdevice::DeviceIndex deviceIdx = testdevice::DeviceIndex::DEVICE_0,
bool fullInfoPrintout = false, bool changingDataset = true);
/**
* This can be used to enable and disable a lot of demo print output.
* @param enable
*/
void enableFullDebugOutput(bool enable);
virtual ~TestDevice();
//! Size of internal buffer used for communication.
static constexpr uint8_t MAX_BUFFER_SIZE = 255;
//! Unique index if the device handler is created multiple times.
testdevice::DeviceIndex deviceIdx = testdevice::DeviceIndex::DEVICE_0;
protected:
testdevice::TestDataSet dataset;
//! This is used to reset the dataset after a commanded change has been made.
bool resetAfterChange = false;
bool commandSent = false;
/** DeviceHandlerBase overrides (see DHB documentation) */
/**
* Hook into the DHB #performOperation call which is executed
* periodically.
*/
void performOperationHook() override;
virtual void doStartUp() override;
virtual void doShutDown() override;
virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
virtual ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t* commandData,
size_t commandDataLen) override;
virtual void fillCommandAndReplyMap() override;
virtual ReturnValue_t scanForReply(const uint8_t* start, size_t len, DeviceCommandId_t* foundId,
size_t* foundLen) override;
virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) override;
virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom) override;
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) override;
/* HasParametersIF overrides */
virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId,
ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues,
uint16_t startAtIndex) override;
uint8_t commandBuffer[MAX_BUFFER_SIZE];
bool fullInfoPrintout = false;
bool oneShot = true;
/* Variables for parameter service */
uint32_t testParameter0 = 0;
int32_t testParameter1 = -2;
float vectorFloatParams2[3] = {};
/* Change device handler functionality, changeable via parameter service */
uint8_t periodicPrintout = false;
uint8_t changingDatasets = false;
ReturnValue_t buildNormalModeCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
size_t commandDataLen);
ReturnValue_t buildTestCommand0(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
size_t commandDataLen);
ReturnValue_t buildTestCommand1(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
size_t commandDataLen);
void passOnCommand(DeviceCommandId_t command, const uint8_t* commandData, size_t commandDataLen);
ReturnValue_t interpretingNormalModeReply();
ReturnValue_t interpretingTestReply0(DeviceCommandId_t id, const uint8_t* packet);
ReturnValue_t interpretingTestReply1(DeviceCommandId_t id, const uint8_t* packet);
ReturnValue_t interpretingTestReply2(DeviceCommandId_t id, const uint8_t* packet);
/* Some timer utilities */
uint8_t divider1 = 2;
PeriodicOperationDivider opDivider1 = PeriodicOperationDivider(divider1);
uint8_t divider2 = 10;
PeriodicOperationDivider opDivider2 = PeriodicOperationDivider(divider2);
static constexpr uint32_t initTimeout = 2000;
Countdown countdown1 = Countdown(initTimeout);
};
#endif /* TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ */

View File

@ -0,0 +1,82 @@
#include "TestEchoComIF.h"
#include <fsfw/serialize/SerializeAdapter.h>
#include <fsfw/serviceinterface/ServiceInterface.h>
#include <fsfw/tmtcpacket/pus/tm.h>
#include <fsfw/tmtcservices/CommandingServiceBase.h>
#include "TestCookie.h"
TestEchoComIF::TestEchoComIF(object_id_t objectId) : SystemObject(objectId) {}
TestEchoComIF::~TestEchoComIF() {}
ReturnValue_t TestEchoComIF::initializeInterface(CookieIF *cookie) {
TestCookie *dummyCookie = dynamic_cast<TestCookie *>(cookie);
if (dummyCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TestEchoComIF::initializeInterface: Invalid cookie!" << std::endl;
#else
sif::printWarning("TestEchoComIF::initializeInterface: Invalid cookie!\n");
#endif
return NULLPOINTER;
}
auto resultPair =
replyMap.emplace(dummyCookie->getAddress(), ReplyBuffer(dummyCookie->getReplyMaxLen()));
if (not resultPair.second) {
return HasReturnvaluesIF::RETURN_FAILED;
}
return RETURN_OK;
}
ReturnValue_t TestEchoComIF::sendMessage(CookieIF *cookie, const uint8_t *sendData,
size_t sendLen) {
TestCookie *dummyCookie = dynamic_cast<TestCookie *>(cookie);
if (dummyCookie == nullptr) {
return NULLPOINTER;
}
ReplyBuffer &replyBuffer = replyMap.find(dummyCookie->getAddress())->second;
if (sendLen > replyBuffer.capacity()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TestEchoComIF::sendMessage: Send length " << sendLen
<< " larger than "
"current reply buffer length!"
<< std::endl;
#else
sif::printWarning(
"TestEchoComIF::sendMessage: Send length %d larger than current "
"reply buffer length!\n",
sendLen);
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
replyBuffer.resize(sendLen);
memcpy(replyBuffer.data(), sendData, sendLen);
return RETURN_OK;
}
ReturnValue_t TestEchoComIF::getSendSuccess(CookieIF *cookie) { return RETURN_OK; }
ReturnValue_t TestEchoComIF::requestReceiveMessage(CookieIF *cookie, size_t requestLen) {
return RETURN_OK;
}
ReturnValue_t TestEchoComIF::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) {
TestCookie *dummyCookie = dynamic_cast<TestCookie *>(cookie);
if (dummyCookie == nullptr) {
return NULLPOINTER;
}
ReplyBuffer &replyBuffer = replyMap.find(dummyCookie->getAddress())->second;
*buffer = replyBuffer.data();
*size = replyBuffer.size();
dummyReplyCounter++;
if (dummyReplyCounter == 10) {
// add anything that needs to be read periodically by dummy handler
dummyReplyCounter = 0;
}
return RETURN_OK;
}

View File

@ -0,0 +1,52 @@
#ifndef TEST_TESTDEVICES_TESTECHOCOMIF_H_
#define TEST_TESTDEVICES_TESTECHOCOMIF_H_
#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/tmtcservices/AcceptsTelemetryIF.h>
#include <vector>
/**
* @brief Used to simply returned sent data from device handler
* @details Assign this com IF in the factory when creating the device handler
* @ingroup test
*/
class TestEchoComIF : public DeviceCommunicationIF, public SystemObject {
public:
TestEchoComIF(object_id_t objectId);
virtual ~TestEchoComIF();
/**
* DeviceCommunicationIF overrides
* (see DeviceCommunicationIF documentation
*/
ReturnValue_t initializeInterface(CookieIF *cookie) override;
ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) override;
ReturnValue_t getSendSuccess(CookieIF *cookie) override;
ReturnValue_t requestReceiveMessage(CookieIF *cookie, size_t requestLen) override;
ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) override;
private:
/**
* Send TM packet which contains received data as TM[17,130].
* Wiretapping will do the same.
* @param data
* @param len
*/
void sendTmPacket(const uint8_t *data, uint32_t len);
AcceptsTelemetryIF *funnel = nullptr;
MessageQueueIF *tmQueue = nullptr;
size_t replyMaxLen = 0;
using ReplyBuffer = std::vector<uint8_t>;
std::map<address_t, ReplyBuffer> replyMap;
uint8_t dummyReplyCounter = 0;
uint16_t packetSubCounter = 0;
};
#endif /* TEST_TESTDEVICES_TESTECHOCOMIF_H_ */

View File

@ -0,0 +1,89 @@
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
namespace testdevice {
enum ParameterUniqueIds : uint8_t {
TEST_UINT32_0,
TEST_INT32_1,
TEST_FLOAT_VEC3_2,
PERIODIC_PRINT_ENABLED,
CHANGING_DATASETS
};
enum DeviceIndex : uint32_t { DEVICE_0, DEVICE_1 };
/** Normal mode command. This ID is also used to access the set variable via the housekeeping
service */
static constexpr DeviceCommandId_t TEST_NORMAL_MODE_CMD = 0;
//! Test completion reply
static constexpr DeviceCommandId_t TEST_COMMAND_0 = 1;
//! Test data reply
static constexpr DeviceCommandId_t TEST_COMMAND_1 = 2;
/**
* Can be used to trigger a notification to the demo controller. For DEVICE_0, only notifications
* messages will be generated while for DEVICE_1, snapshot messages will be generated.
*
* DEVICE_0 VAR: Sets the set variable 0 above a treshold (200) to trigger a variable
* notification.
* DEVICE_0 SET: Sets the vector mean values above a treshold (mean larger than 20) to trigger a
* set notification.
*
* DEVICE_1 VAR: Sets the set variable 0 below a treshold (less than 50 but not 0) to trigger a
* variable snapshot.
* DEVICE_1 SET: Sets the set vector mean values below a treshold (mean smaller than -20) to
* trigger a set snapshot message.
*/
static constexpr DeviceCommandId_t TEST_NOTIF_SNAPSHOT_VAR = 3;
static constexpr DeviceCommandId_t TEST_NOTIF_SNAPSHOT_SET = 4;
/**
* Can be used to trigger a snapshot message to the demo controller.
* Depending on the device index, a notification will be triggered for different set variables.
*
* DEVICE_0: Sets the set variable 0 below a treshold (below 50 but not 0) to trigger
* a variable snapshot
* DEVICE_1: Sets the vector mean values below a treshold (mean less than -20) to trigger a
* set snapshot
*/
static constexpr DeviceCommandId_t TEST_SNAPSHOT = 5;
//! Generates a random value for variable 1 of the dataset.
static constexpr DeviceCommandId_t GENERATE_SET_VAR_1_RNG_VALUE = 6;
/**
* These parameters are sent back with the command ID as a data reply
*/
static constexpr uint16_t COMMAND_1_PARAM1 = 0xBAB0; //!< param1, 2 bytes
//! param2, 8 bytes
static constexpr uint64_t COMMAND_1_PARAM2 = 0x000000524F42494E;
static constexpr size_t TEST_COMMAND_0_SIZE = sizeof(TEST_COMMAND_0);
static constexpr size_t TEST_COMMAND_1_SIZE =
sizeof(TEST_COMMAND_1) + sizeof(COMMAND_1_PARAM1) + sizeof(COMMAND_1_PARAM2);
enum PoolIds : lp_id_t { TEST_UINT8_ID = 0, TEST_UINT32_ID = 1, TEST_FLOAT_VEC_3_ID = 2 };
static constexpr uint8_t TEST_SET_ID = TEST_NORMAL_MODE_CMD;
class TestDataSet : public StaticLocalDataSet<3> {
public:
TestDataSet(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, TEST_SET_ID) {}
TestDataSet(object_id_t owner) : StaticLocalDataSet(sid_t(owner, TEST_SET_ID)) {}
lp_var_t<uint8_t> testUint8Var =
lp_var_t<uint8_t>(gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_UINT8_ID), this);
lp_var_t<uint32_t> testUint32Var =
lp_var_t<uint32_t>(gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_UINT32_ID), this);
lp_vec_t<float, 3> testFloat3Vec =
lp_vec_t<float, 3>(gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_FLOAT_VEC_3_ID), this);
};
} // namespace testdevice
#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ */

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
TestTask.cpp
)

View File

@ -0,0 +1,62 @@
#include "TestTask.h"
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/serviceinterface/ServiceInterface.h>
MutexIF* TestTask::testLock = nullptr;
TestTask::TestTask(object_id_t objectId) : SystemObject(objectId), testMode(testModes::A) {
if (testLock == nullptr) {
testLock = MutexFactory::instance()->createMutex();
}
IPCStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
}
TestTask::~TestTask() = default;
ReturnValue_t TestTask::performOperation(uint8_t operationCode) {
ReturnValue_t result = RETURN_OK;
testLock->lockMutex(MutexIF::TimeoutType::WAITING, 20);
if (oneShotAction) {
// Add code here which should only be run once
performOneShotAction();
oneShotAction = false;
}
testLock->unlockMutex();
// Add code here which should only be run once per performOperation
performPeriodicAction();
// Add code here which should only be run on alternating cycles.
if (testMode == testModes::A) {
performActionA();
testMode = testModes::B;
} else if (testMode == testModes::B) {
performActionB();
testMode = testModes::A;
}
return result;
}
ReturnValue_t TestTask::performOneShotAction() {
/* Everything here will only be performed once. */
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t TestTask::performPeriodicAction() {
/* This is performed each task cycle */
ReturnValue_t result = RETURN_OK;
return result;
}
ReturnValue_t TestTask::performActionA() {
/* This is performed each alternating task cycle */
ReturnValue_t result = RETURN_OK;
return result;
}
ReturnValue_t TestTask::performActionB() {
/* This is performed each alternating task cycle */
ReturnValue_t result = RETURN_OK;
return result;
}

View File

@ -0,0 +1,37 @@
#ifndef MISSION_DEMO_TESTTASK_H_
#define MISSION_DEMO_TESTTASK_H_
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/storagemanager/StorageManagerIF.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
/**
* @brief Test class for general C++ testing and any other code which will not be part of the
* primary mission software.
* @details
* Should not be used for board specific tests. Instead, a derived board test class should be used.
*/
class TestTask : public SystemObject, public ExecutableObjectIF, public HasReturnvaluesIF {
public:
explicit TestTask(object_id_t objectId);
~TestTask() override;
ReturnValue_t performOperation(uint8_t operationCode) override;
protected:
virtual ReturnValue_t performOneShotAction();
virtual ReturnValue_t performPeriodicAction();
virtual ReturnValue_t performActionA();
virtual ReturnValue_t performActionB();
enum testModes : uint8_t { A, B };
testModes testMode;
bool testFlag = false;
private:
bool oneShotAction = true;
static MutexIF* testLock;
StorageManagerIF* IPCStore;
};
#endif /* TESTTASK_H_ */

View File

@ -0,0 +1,8 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
InternalUnitTester.cpp
UnittDefinitions.cpp
)
add_subdirectory(osal)
add_subdirectory(serialize)
add_subdirectory(globalfunctions)

View File

@ -0,0 +1,41 @@
#include "fsfw_tests/internal/InternalUnitTester.h"
#include <cstdlib>
#include "fsfw_tests/internal/UnittDefinitions.h"
#include "fsfw_tests/internal/globalfunctions/TestArrayPrinter.h"
#include "fsfw_tests/internal/osal/testMq.h"
#include "fsfw_tests/internal/osal/testMutex.h"
#include "fsfw_tests/internal/osal/testSemaphore.h"
#include "fsfw_tests/internal/serialize/IntTestSerialization.h"
InternalUnitTester::InternalUnitTester() {}
InternalUnitTester::~InternalUnitTester() {}
ReturnValue_t InternalUnitTester::performTests(
const struct InternalUnitTester::TestConfig& testConfig) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Running internal unit tests.. Error messages might follow" << std::endl;
#else
sif::printInfo("Running internal unit tests..\n");
#endif
testserialize::test_serialization();
testmq::testMq();
if (testConfig.testSemaphores) {
testsemaph::testBinSemaph();
testsemaph::testCountingSemaph();
}
testmutex::testMutex();
if (testConfig.testArrayPrinter) {
arrayprinter::testArrayPrinter();
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Internal unit tests finished." << std::endl;
#else
sif::printInfo("Internal unit tests finished.\n");
#endif
return RETURN_OK;
}

View File

@ -0,0 +1,33 @@
#ifndef FRAMEWORK_TEST_UNITTESTCLASS_H_
#define FRAMEWORK_TEST_UNITTESTCLASS_H_
#include "UnittDefinitions.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
/**
* @brief Can be used for internal testing, for example for hardware specific
* tests which can not be run on a host-machine.
*
* TODO: A lot of ways to improve this class. A way for tests to subscribe
* in this central class would be nice. Right now, this is the class
* which simply calls all other tests from other files manually.
* Maybe there is a better way..
*/
class InternalUnitTester : public HasReturnvaluesIF {
public:
struct TestConfig {
bool testArrayPrinter = false;
bool testSemaphores = true;
};
InternalUnitTester();
virtual ~InternalUnitTester();
/**
* Some function which calls all other tests
* @return
*/
virtual ReturnValue_t performTests(const struct InternalUnitTester::TestConfig& testConfig);
};
#endif /* FRAMEWORK_TEST_UNITTESTCLASS_H_ */

View File

@ -0,0 +1,10 @@
#include "fsfw_tests/internal/UnittDefinitions.h"
ReturnValue_t unitt::put_error(std::string errorId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Unit Tester error: Failed at test ID " << errorId << std::endl;
#else
sif::printError("Unit Tester error: Failed at test ID %s\n", errorId.c_str());
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return HasReturnvaluesIF::RETURN_FAILED;
}

View File

@ -0,0 +1,33 @@
#ifndef UNITTEST_INTERNAL_UNITTDEFINITIONS_H_
#define UNITTEST_INTERNAL_UNITTDEFINITIONS_H_
#include <cstddef>
#include <cstdint>
#include <string>
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
namespace tv {
// POD test values
static const bool tv_bool = true;
static const uint8_t tv_uint8{5};
static const uint16_t tv_uint16{283};
static const uint32_t tv_uint32{929221};
static const uint64_t tv_uint64{2929329429};
static const int8_t tv_int8{-16};
static const int16_t tv_int16{-829};
static const int32_t tv_int32{-2312};
static const float tv_float{8.2149214};
static const float tv_sfloat = {-922.2321321};
static const double tv_double{9.2132142141e8};
static const double tv_sdouble{-2.2421e19};
} // namespace tv
namespace unitt {
ReturnValue_t put_error(std::string errorId);
}
#endif /* UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ */

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
TestArrayPrinter.cpp
)

View File

@ -0,0 +1,28 @@
#include "fsfw_tests/internal/globalfunctions/TestArrayPrinter.h"
void arrayprinter::testArrayPrinter() {
{
const std::array<uint8_t, 5> testDataSmall = {0x01, 0x02, 0x03, 0x04, 0x05};
arrayprinter::print(testDataSmall.data(), testDataSmall.size());
arrayprinter::print(testDataSmall.data(), testDataSmall.size(), OutputType::DEC);
arrayprinter::print(testDataSmall.data(), testDataSmall.size(), OutputType::BIN);
}
{
std::array<uint8_t, 16> testDataMed;
for (size_t idx = 0; idx < testDataMed.size(); idx++) {
testDataMed[idx] = testDataMed.size() - idx;
}
arrayprinter::print(testDataMed.data(), testDataMed.size());
arrayprinter::print(testDataMed.data(), testDataMed.size(), OutputType::DEC, 8);
}
{
std::array<uint8_t, 32> testDataLarge;
for (size_t idx = 0; idx < testDataLarge.size(); idx++) {
testDataLarge[idx] = idx;
}
arrayprinter::print(testDataLarge.data(), testDataLarge.size());
arrayprinter::print(testDataLarge.data(), testDataLarge.size(), OutputType::DEC);
}
}

View File

@ -0,0 +1,14 @@
#ifndef FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_
#define FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_
#include <fsfw/globalfunctions/arrayprinter.h>
#include <array>
namespace arrayprinter {
void testArrayPrinter();
}
#endif /* FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_ */

View File

@ -0,0 +1,5 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
testMq.cpp
testMutex.cpp
testSemaphore.cpp
)

View File

@ -0,0 +1,50 @@
#include "testMq.h"
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <array>
#include "fsfw_tests/internal/UnittDefinitions.h"
using retval = HasReturnvaluesIF;
void testmq::testMq() {
std::string id = "[testMq]";
MessageQueueIF* testSenderMq = QueueFactory::instance()->createMessageQueue(1);
MessageQueueId_t testSenderMqId = testSenderMq->getId();
MessageQueueIF* testReceiverMq = QueueFactory::instance()->createMessageQueue(1);
MessageQueueId_t testReceiverMqId = testReceiverMq->getId();
std::array<uint8_t, 20> testData{0};
testData[0] = 42;
MessageQueueMessage testMessage(testData.data(), 1);
testSenderMq->setDefaultDestination(testReceiverMqId);
auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
if (result != retval::RETURN_OK) {
unitt::put_error(id);
}
MessageQueueMessage recvMessage;
result = testReceiverMq->receiveMessage(&recvMessage);
if (result != retval::RETURN_OK or recvMessage.getData()[0] != 42) {
unitt::put_error(id);
}
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
if (result != retval::RETURN_OK) {
unitt::put_error(id);
}
MessageQueueId_t senderId = 0;
result = testReceiverMq->receiveMessage(&recvMessage, &senderId);
if (result != retval::RETURN_OK or recvMessage.getData()[0] != 42) {
unitt::put_error(id);
}
if (senderId != testSenderMqId) {
unitt::put_error(id);
}
senderId = testReceiverMq->getLastPartner();
if (senderId != testSenderMqId) {
unitt::put_error(id);
}
}

View File

@ -0,0 +1,8 @@
#ifndef UNITTEST_INTERNAL_INTESTMQ_H_
#define UNITTEST_INTERNAL_INTESTMQ_H_
namespace testmq {
void testMq();
}
#endif /* UNITTEST_INTERNAL_INTESTMQ_H_ */

View File

@ -0,0 +1,48 @@
#include "testMutex.h"
#include <fsfw/ipc/MutexFactory.h>
#include "fsfw/platform.h"
#include "fsfw_tests/internal/UnittDefinitions.h"
#if defined PLATFORM_WIN || defined PLATFORM_UNIX
#include <future>
#include <thread>
#include "fsfw/osal/host/Mutex.h"
#endif
void testmutex::testMutex() {
std::string id = "[testMutex]";
MutexIF* mutex = MutexFactory::instance()->createMutex();
auto result = mutex->lockMutex(MutexIF::TimeoutType::POLLING);
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
// timed_mutex from the C++ library specifies undefined behaviour if
// the timed mutex is locked twice from the same thread.
// TODO: we should pass a define like FSFW_OSAL_HOST to the build.
#if defined PLATFORM_WIN || defined PLATFORM_UNIX
// This calls the function from
// another thread and stores the returnvalue in a future.
auto future = std::async(&MutexIF::lockMutex, mutex, MutexIF::TimeoutType::WAITING, 1);
result = future.get();
#else
result = mutex->lockMutex(MutexIF::TimeoutType::WAITING, 1);
#endif
if (result != MutexIF::MUTEX_TIMEOUT) {
unitt::put_error(id);
}
result = mutex->unlockMutex();
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
#if !defined PLATFORM_WIN && !defined PLATFORM_UNIX
result = mutex->unlockMutex();
if (result != MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX) {
unitt::put_error(id);
}
#endif
}

View File

@ -0,0 +1,8 @@
#ifndef UNITTEST_INTERNAL_INTTESTMUTEX_H_
#define UNITTEST_INTERNAL_INTTESTMUTEX_H_
namespace testmutex {
void testMutex();
}
#endif /* UNITTEST_INTERNAL_INTTESTMUTEX_H_ */

View File

@ -0,0 +1,155 @@
#include "testSemaphore.h"
#include <cstdlib>
#include "fsfw/FSFW.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/tasks/SemaphoreFactory.h"
#include "fsfw/timemanager/Stopwatch.h"
#include "fsfw_tests/internal/UnittDefinitions.h"
void testsemaph::testBinSemaph() {
std::string id = "[BinSemaphore]";
SemaphoreIF* binSemaph = SemaphoreFactory::instance()->createBinarySemaphore();
if (binSemaph == nullptr) {
return;
}
testBinSemaphoreImplementation(binSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(binSemaph);
#if defined FSFW_OSAL_FREERTOS
SemaphoreIF* binSemaphUsingTask = SemaphoreFactory::instance()->createBinarySemaphore(1);
testBinSemaphoreImplementation(binSemaphUsingTask, id);
SemaphoreFactory::instance()->deleteSemaphore(binSemaphUsingTask);
#endif
}
void testsemaph::testCountingSemaph() {
std::string id = "[CountingSemaph]";
{
// First test: create a binary semaphore by using a counting semaphore.
SemaphoreIF* countingSemaph = SemaphoreFactory::instance()->createCountingSemaphore(1, 1);
if (countingSemaph == nullptr) {
return;
}
testBinSemaphoreImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#if defined FSFW_OSAL_FREERTOS
countingSemaph = SemaphoreFactory::instance()->createCountingSemaphore(1, 1, 1);
testBinSemaphoreImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#endif
}
{
// Second test: counting semaphore with count 3 and init count of 3.
SemaphoreIF* countingSemaph = SemaphoreFactory::instance()->createCountingSemaphore(3, 3);
testCountingSemaphImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#if defined FSFW_OSAL_FREERTOS
countingSemaph = SemaphoreFactory::instance()->createCountingSemaphore(3, 0, 1);
uint8_t semaphCount = countingSemaph->getSemaphoreCounter();
if (semaphCount != 0) {
unitt::put_error(id);
}
// release 3 times in a row
for (int i = 0; i < 3; i++) {
auto result = countingSemaph->release();
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
testCountingSemaphImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#endif
}
}
void testsemaph::testBinSemaphoreImplementation(SemaphoreIF* binSemaph, std::string id) {
uint8_t semaphCount = binSemaph->getSemaphoreCounter();
if (semaphCount != 1) {
unitt::put_error(id);
}
ReturnValue_t result = binSemaph->release();
if (result != SemaphoreIF::SEMAPHORE_NOT_OWNED) {
unitt::put_error(id);
}
result = binSemaph->acquire(SemaphoreIF::BLOCKING);
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
// There is not really a point in testing time related, the task
// might get interrupted..
{
// Stopwatch stopwatch(false);
result = binSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 10);
// dur_millis_t time = stopwatch.stop();
// if(abs(time - 10) > 2) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
// sif::error << "UnitTester: Semaphore timeout measured incorrect."
// << std::endl;
#endif
// unitt::put_error(id);
// }
}
if (result != SemaphoreIF::SEMAPHORE_TIMEOUT) {
unitt::put_error(id);
}
semaphCount = binSemaph->getSemaphoreCounter();
if (semaphCount != 0) {
unitt::put_error(id);
}
result = binSemaph->release();
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
void testsemaph::testCountingSemaphImplementation(SemaphoreIF* countingSemaph, std::string id) {
// check count getter function
uint8_t semaphCount = countingSemaph->getSemaphoreCounter();
if (semaphCount != 3) {
unitt::put_error(id);
}
ReturnValue_t result = countingSemaph->release();
if (result != SemaphoreIF::SEMAPHORE_NOT_OWNED) {
unitt::put_error(id);
}
// acquire 3 times in a row
for (int i = 0; i < 3; i++) {
result = countingSemaph->acquire(SemaphoreIF::BLOCKING);
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
{
Stopwatch stopwatch(false);
// attempt to take when count is 0, measure time
result = countingSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 10);
dur_millis_t time = stopwatch.stop();
if (std::abs(static_cast<int32_t>(time - 10)) > 1) {
unitt::put_error(id);
}
}
if (result != SemaphoreIF::SEMAPHORE_TIMEOUT) {
unitt::put_error(id);
}
// release 3 times in a row
for (int i = 0; i < 3; i++) {
result = countingSemaph->release();
if (result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
// assert correct full count
if (countingSemaph->getSemaphoreCounter() != 3) {
unitt::put_error(id);
}
}

View File

@ -0,0 +1,13 @@
#ifndef UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_
#define UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_
class SemaphoreIF;
#include <string>
namespace testsemaph {
void testBinSemaph();
void testBinSemaphoreImplementation(SemaphoreIF* binSemaph, std::string id);
void testCountingSemaph();
void testCountingSemaphImplementation(SemaphoreIF* countingSemaph, std::string id);
} // namespace testsemaph
#endif /* UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ */

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
IntTestSerialization.cpp
)

View File

@ -0,0 +1,210 @@
#include "fsfw_tests/internal/serialize/IntTestSerialization.h"
#include <fsfw/serialize/SerialBufferAdapter.h>
#include <fsfw/serialize/SerializeElement.h>
#include <fsfw/serialize/SerializeIF.h>
#include <array>
#include "fsfw_tests/internal/UnittDefinitions.h"
using retval = HasReturnvaluesIF;
std::array<uint8_t, 512> testserialize::test_array = {0};
ReturnValue_t testserialize::test_serialization() {
// Here, we test all serialization tools. First test basic cases.
ReturnValue_t result = test_endianness_tools();
if (result != retval::RETURN_OK) {
return result;
}
result = test_autoserialization();
if (result != retval::RETURN_OK) {
return result;
}
result = test_serial_buffer_adapter();
if (result != retval::RETURN_OK) {
return result;
}
return retval::RETURN_OK;
}
ReturnValue_t testserialize::test_endianness_tools() {
std::string id = "[test_endianness_tools]";
test_array[0] = 0;
test_array[1] = 0;
uint16_t two_byte_value = 1;
size_t size = 0;
uint8_t* p_array = test_array.data();
SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2,
SerializeIF::Endianness::MACHINE);
// Little endian: Value one on first byte
if (test_array[0] != 1 and test_array[1] != 0) {
return unitt::put_error(id);
}
p_array = test_array.data();
size = 0;
SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2, SerializeIF::Endianness::BIG);
// Big endian: Value one on second byte
if (test_array[0] != 0 and test_array[1] != 1) {
return unitt::put_error(id);
}
return retval::RETURN_OK;
}
ReturnValue_t testserialize::test_autoserialization() {
std::string id = "[test_autoserialization]";
// Unit Test getSerializedSize
if (SerializeAdapter::getSerializedSize(&tv::tv_bool) != sizeof(tv::tv_bool) or
SerializeAdapter::getSerializedSize(&tv::tv_uint8) != sizeof(tv::tv_uint8) or
SerializeAdapter::getSerializedSize(&tv::tv_uint16) != sizeof(tv::tv_uint16) or
SerializeAdapter::getSerializedSize(&tv::tv_uint32) != sizeof(tv::tv_uint32) or
SerializeAdapter::getSerializedSize(&tv::tv_uint64) != sizeof(tv::tv_uint64) or
SerializeAdapter::getSerializedSize(&tv::tv_int8) != sizeof(tv::tv_int8) or
SerializeAdapter::getSerializedSize(&tv::tv_double) != sizeof(tv::tv_double) or
SerializeAdapter::getSerializedSize(&tv::tv_int16) != sizeof(tv::tv_int16) or
SerializeAdapter::getSerializedSize(&tv::tv_int32) != sizeof(tv::tv_int32) or
SerializeAdapter::getSerializedSize(&tv::tv_float) != sizeof(tv::tv_float)) {
return unitt::put_error(id);
}
size_t serialized_size = 0;
uint8_t* p_array = test_array.data();
SerializeAdapter::serialize(&tv::tv_bool, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint8, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint16, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint32, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_int8, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_int16, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_int32, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint64, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_float, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_double, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_sfloat, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_sdouble, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
// expected size is 1 + 1 + 2 + 4 + 1 + 2 + 4 + 8 + 4 + 8 + 4 + 8
if (serialized_size != 47) {
return unitt::put_error(id);
}
p_array = test_array.data();
size_t remaining_size = serialized_size;
bool tv_bool;
uint8_t tv_uint8;
uint16_t tv_uint16;
uint32_t tv_uint32;
int8_t tv_int8;
int16_t tv_int16;
int32_t tv_int32;
uint64_t tv_uint64;
float tv_float;
double tv_double;
float tv_sfloat;
double tv_sdouble;
SerializeAdapter::deSerialize(&tv_bool, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint8, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint16, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint32, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_int8, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_int16, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_int32, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint64, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_float, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_double, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_sfloat, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_sdouble, const_cast<const uint8_t**>(&p_array), &remaining_size,
SerializeIF::Endianness::MACHINE);
if (tv_bool != tv::tv_bool or tv_uint8 != tv::tv_uint8 or tv_uint16 != tv::tv_uint16 or
tv_uint32 != tv::tv_uint32 or tv_uint64 != tv::tv_uint64 or tv_int8 != tv::tv_int8 or
tv_int16 != tv::tv_int16 or tv_int32 != tv::tv_int32) {
return unitt::put_error(id);
}
// These epsilon values were just guessed.. It appears to work though.
if (abs(tv_float - tv::tv_float) > 0.0001 or abs(tv_double - tv::tv_double) > 0.01 or
abs(tv_sfloat - tv::tv_sfloat) > 0.0001 or abs(tv_sdouble - tv::tv_sdouble) > 0.01) {
return unitt::put_error(id);
}
// Check overflow
return retval::RETURN_OK;
}
// TODO: Also test for constant buffers.
ReturnValue_t testserialize::test_serial_buffer_adapter() {
std::string id = "[test_serial_buffer_adapter]";
// I will skip endian swapper testing, its going to be changed anyway..
// uint8_t tv::tv_uint8_swapped = EndianSwapper::swap(tv::tv_uint8);
size_t serialized_size = 0;
uint8_t* p_array = test_array.data();
std::array<uint8_t, 5> test_serial_buffer{5, 4, 3, 2, 1};
SerialBufferAdapter<uint8_t> tv_serial_buffer_adapter =
SerialBufferAdapter<uint8_t>(test_serial_buffer.data(), test_serial_buffer.size(), false);
uint16_t testUint16 = 16;
SerializeAdapter::serialize(&tv::tv_bool, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv_serial_buffer_adapter, &p_array, &serialized_size,
test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
if (serialized_size != 8 or test_array[0] != true or test_array[1] != 5 or test_array[2] != 4 or
test_array[3] != 3 or test_array[4] != 2 or test_array[5] != 1) {
return unitt::put_error(id);
}
memcpy(&testUint16, test_array.data() + 6, sizeof(testUint16));
if (testUint16 != 16) {
return unitt::put_error(id);
}
// Serialize with size field
SerialBufferAdapter<uint8_t> tv_serial_buffer_adapter2 =
SerialBufferAdapter<uint8_t>(test_serial_buffer.data(), test_serial_buffer.size(), true);
serialized_size = 0;
p_array = test_array.data();
SerializeAdapter::serialize(&tv::tv_bool, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv_serial_buffer_adapter2, &p_array, &serialized_size,
test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size, test_array.size(),
SerializeIF::Endianness::MACHINE);
if (serialized_size != 9 or test_array[0] != true or test_array[1] != 5 or test_array[2] != 5 or
test_array[3] != 4 or test_array[4] != 3 or test_array[5] != 2 or test_array[6] != 1) {
return unitt::put_error(id);
}
memcpy(&testUint16, test_array.data() + 7, sizeof(testUint16));
if (testUint16 != 16) {
return unitt::put_error(id);
}
return retval::RETURN_OK;
}

View File

@ -0,0 +1,17 @@
#ifndef FSFW_UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_
#define FSFW_UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_
#include <array>
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
namespace testserialize {
ReturnValue_t test_serialization();
ReturnValue_t test_endianness_tools();
ReturnValue_t test_autoserialization();
ReturnValue_t test_serial_buffer_adapter();
extern std::array<uint8_t, 512> test_array;
} // namespace testserialize
#endif /* FSFW_UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */

View File

@ -0,0 +1,29 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
CatchDefinitions.cpp
CatchFactory.cpp
printChar.cpp
version.cpp
)
target_sources(${FSFW_TEST_TGT} PRIVATE
CatchRunner.cpp
CatchSetup.cpp
)
add_subdirectory(testcfg)
add_subdirectory(mocks)
add_subdirectory(action)
add_subdirectory(power)
add_subdirectory(container)
add_subdirectory(osal)
add_subdirectory(serialize)
add_subdirectory(datapoollocal)
add_subdirectory(storagemanager)
add_subdirectory(globalfunctions)
add_subdirectory(timemanager)
add_subdirectory(tmtcpacket)
add_subdirectory(cfdp)
add_subdirectory(hal)
add_subdirectory(internalerror)
add_subdirectory(devicehandler)

View File

@ -0,0 +1,17 @@
#include "CatchDefinitions.h"
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/serviceinterface/ServiceInterface.h>
StorageManagerIF* tglob::getIpcStoreHandle() {
if (ObjectManager::instance() != nullptr) {
return ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
} else {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Global object manager uninitialized" << std::endl;
#else
sif::printError("Global object manager uninitialized\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return nullptr;
}
}

View File

@ -0,0 +1,21 @@
#ifndef FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_
#define FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_
#include <fsfw/ipc/messageQueueDefinitions.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <fsfw/storagemanager/StorageManagerIF.h>
namespace retval {
static constexpr int CATCH_OK = static_cast<int>(HasReturnvaluesIF::RETURN_OK);
static constexpr int CATCH_FAILED = static_cast<int>(HasReturnvaluesIF::RETURN_FAILED);
} // namespace retval
namespace tconst {
static constexpr MessageQueueId_t testQueueId = 42;
}
namespace tglob {
StorageManagerIF* getIpcStoreHandle();
}
#endif /* FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ */

View File

@ -0,0 +1,77 @@
#include "CatchFactory.h"
#include <fsfw/datapoollocal/LocalDataPoolManager.h>
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw/events/EventManager.h>
#include <fsfw/health/HealthTable.h>
#include <fsfw/internalerror/InternalErrorReporter.h>
#include <fsfw/objectmanager/frameworkObjects.h>
#include <fsfw/storagemanager/PoolManager.h>
#include <fsfw/tmtcpacket/pus/tm/TmPacketStored.h>
#include <fsfw/tmtcservices/CommandingServiceBase.h>
#include <fsfw/tmtcservices/PusServiceBase.h>
#include "datapoollocal/LocalPoolOwnerBase.h"
#include "mocks/HkReceiverMock.h"
#include "tests/TestsConfig.h"
#if FSFW_ADD_DEFAULT_FACTORY_FUNCTIONS == 1
/**
* @brief Produces system objects.
* @details
* Build tasks by using SystemObject Interface (Interface).
* Header files of all tasks must be included
* Please note that an object has to implement the system object interface
* if the interface validity is checked or retrieved later by using the
* get<TargetInterface>(object_id) function from the ObjectManagerIF.
*
* Framework objects are created first.
*
* @ingroup init
*/
void Factory::produceFrameworkObjects(void* args) {
setStaticFrameworkObjectIds();
new EventManager(objects::EVENT_MANAGER);
new HealthTable(objects::HEALTH_TABLE);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);
new LocalPoolOwnerBase(objects::TEST_LOCAL_POOL_OWNER_BASE);
new HkReceiverMock(objects::HK_RECEIVER_MOCK);
{
PoolManager::LocalPoolConfig poolCfg = {{100, 16}, {50, 32}, {25, 64}, {15, 128}, {5, 1024}};
new PoolManager(objects::TC_STORE, poolCfg);
}
{
PoolManager::LocalPoolConfig poolCfg = {{100, 16}, {50, 32}, {25, 64}, {15, 128}, {5, 1024}};
new PoolManager(objects::TM_STORE, poolCfg);
}
{
PoolManager::LocalPoolConfig poolCfg = {{100, 16}, {50, 32}, {25, 64}, {15, 128}, {5, 1024}};
new PoolManager(objects::IPC_STORE, poolCfg);
}
}
void Factory::setStaticFrameworkObjectIds() {
PusServiceBase::packetSource = objects::NO_OBJECT;
PusServiceBase::packetDestination = objects::NO_OBJECT;
CommandingServiceBase::defaultPacketSource = objects::NO_OBJECT;
CommandingServiceBase::defaultPacketDestination = objects::NO_OBJECT;
VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION;
DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT;
DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT;
LocalDataPoolManager::defaultHkDestination = objects::HK_RECEIVER_MOCK;
DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT;
TmPacketBase::timeStamperId = objects::NO_OBJECT;
}
#endif

View File

@ -0,0 +1,24 @@
#ifndef FSFW_CATCHFACTORY_H_
#define FSFW_CATCHFACTORY_H_
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/objectmanager/SystemObjectIF.h"
#include "tests/TestsConfig.h"
// TODO: It is possible to solve this more cleanly using a special class which
// is allowed to set the object IDs and has virtual functions.
#if FSFW_ADD_DEFAULT_FACTORY_FUNCTIONS == 1
namespace Factory {
/**
* @brief Creates all SystemObject elements which are persistent
* during execution.
*/
void produceFrameworkObjects(void* args);
void setStaticFrameworkObjectIds();
} // namespace Factory
#endif /* FSFW_ADD_DEFAULT_FSFW_FACTORY == 1 */
#endif /* FSFW_CATCHFACTORY_H_ */

View File

@ -0,0 +1,25 @@
/**
* @file CatchRunner.cpp
* @brief Source file to compile catch framework.
* @details All tests should be written in other files.
* For eclipse console output, install ANSI Escape in Console
* from the eclipse market place to get colored characters.
*/
#include "CatchRunner.h"
#define CATCH_CONFIG_COLOUR_WINDOWS
#include <catch2/catch_session.hpp>
extern int customSetup();
int main(int argc, char* argv[]) {
customSetup();
// Catch internal function call
int result = Catch::Session().run(argc, argv);
// global clean-up
return result;
}

View File

@ -0,0 +1,14 @@
#ifndef FSFW_TESTS_USER_UNITTEST_CORE_CATCHRUNNER_H_
#define FSFW_TESTS_USER_UNITTEST_CORE_CATCHRUNNER_H_
namespace fsfwtest {
/**
* Can be called by upper level main() if default Catch2 main is overriden
* @return
*/
// int customMain(int argc, char* argv[]);
} // namespace fsfwtest
#endif /* FSFW_TESTS_USER_UNITTEST_CORE_CATCHRUNNER_H_ */

View File

@ -0,0 +1,31 @@
#include "CatchDefinitions.h"
#include "CatchFactory.h"
#ifdef GCOV
#include <gcov.h>
#endif
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/storagemanager/StorageManagerIF.h"
/* Global instantiations normally done in main.cpp */
/* Initialize Data Pool */
#if FSFW_CPP_OSTREAM_ENABLED == 1
namespace sif {
/* Set up output streams */
ServiceInterfaceStream debug("DEBUG");
ServiceInterfaceStream info("INFO");
ServiceInterfaceStream error("ERROR");
ServiceInterfaceStream warning("WARNING");
} // namespace sif
#endif
int customSetup() {
// global setup
ObjectManager* objMan = ObjectManager::instance();
objMan->setObjectFactoryFunction(Factory::produceFrameworkObjects, nullptr);
objMan->initialize();
return 0;
}

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
TestActionHelper.cpp
)

View File

@ -0,0 +1,114 @@
#include "TestActionHelper.h"
#include <fsfw/action/ActionHelper.h>
#include <fsfw/ipc/CommandMessage.h>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/mocks/MessageQueueMockBase.h"
TEST_CASE("Action Helper", "[ActionHelper]") {
ActionHelperOwnerMockBase testDhMock;
MessageQueueMockBase testMqMock;
ActionHelper actionHelper = ActionHelper(&testDhMock, dynamic_cast<MessageQueueIF*>(&testMqMock));
CommandMessage actionMessage;
ActionId_t testActionId = 777;
std::array<uint8_t, 3> testParams{1, 2, 3};
store_address_t paramAddress;
StorageManagerIF* ipcStore = tglob::getIpcStoreHandle();
REQUIRE(ipcStore != nullptr);
ipcStore->addData(&paramAddress, testParams.data(), 3);
REQUIRE(actionHelper.initialize() == retval::CATCH_OK);
SECTION("Simple tests") {
ActionMessage::setCommand(&actionMessage, testActionId, paramAddress);
CHECK(not testDhMock.executeActionCalled);
REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK);
CHECK(testDhMock.executeActionCalled);
// No message is sent if everything is alright.
CHECK(not testMqMock.wasMessageSent());
store_address_t invalidAddress;
ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress);
actionHelper.handleActionMessage(&actionMessage);
CHECK(testMqMock.wasMessageSent());
const uint8_t* ptr = nullptr;
size_t size = 0;
REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) ==
static_cast<uint32_t>(StorageManagerIF::DATA_DOES_NOT_EXIST));
REQUIRE(ptr == nullptr);
REQUIRE(size == 0);
testDhMock.getBuffer(&ptr, &size);
REQUIRE(size == 3);
for (uint8_t i = 0; i < 3; i++) {
REQUIRE(ptr[i] == (i + 1));
}
testDhMock.clearBuffer();
}
SECTION("Handle failures") {
actionMessage.setCommand(1234);
REQUIRE(actionHelper.handleActionMessage(&actionMessage) ==
static_cast<uint32_t>(CommandMessage::UNKNOWN_COMMAND));
CHECK(not testMqMock.wasMessageSent());
uint16_t step = 5;
ReturnValue_t status = 0x1234;
actionHelper.step(step, testMqMock.getId(), testActionId, status);
step += 1;
CHECK(testMqMock.wasMessageSent());
CommandMessage testMessage;
REQUIRE(testMqMock.receiveMessage(&testMessage) ==
static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
REQUIRE(testMessage.getParameter() == static_cast<uint32_t>(testActionId));
uint32_t parameter2 = ((uint32_t)step << 16) | (uint32_t)status;
REQUIRE(testMessage.getParameter2() == parameter2);
REQUIRE(ActionMessage::getStep(&testMessage) == step);
}
SECTION("Handle finish") {
CHECK(not testMqMock.wasMessageSent());
ReturnValue_t status = 0x9876;
actionHelper.finish(false, testMqMock.getId(), testActionId, status);
CHECK(testMqMock.wasMessageSent());
CommandMessage testMessage;
REQUIRE(testMqMock.receiveMessage(&testMessage) ==
static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::COMPLETION_FAILED));
REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId);
REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast<uint32_t>(status));
}
SECTION("Handle failed") {
store_address_t toLongParamAddress = StorageManagerIF::INVALID_ADDRESS;
std::array<uint8_t, 5> toLongData = {5, 4, 3, 2, 1};
REQUIRE(ipcStore->addData(&toLongParamAddress, toLongData.data(), 5) == retval::CATCH_OK);
ActionMessage::setCommand(&actionMessage, testActionId, toLongParamAddress);
CHECK(not testDhMock.executeActionCalled);
REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK);
REQUIRE(ipcStore->getData(toLongParamAddress).first ==
static_cast<uint32_t>(StorageManagerIF::DATA_DOES_NOT_EXIST));
CommandMessage testMessage;
REQUIRE(testMqMock.receiveMessage(&testMessage) ==
static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
REQUIRE(ActionMessage::getReturnCode(&testMessage) == 0xAFFE);
REQUIRE(ActionMessage::getStep(&testMessage) == 0);
REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId);
}
SECTION("Missing IPC Data") {
ActionMessage::setCommand(&actionMessage, testActionId, StorageManagerIF::INVALID_ADDRESS);
CHECK(not testDhMock.executeActionCalled);
REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK);
CommandMessage testMessage;
REQUIRE(testMqMock.receiveMessage(&testMessage) ==
static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
REQUIRE(ActionMessage::getReturnCode(&testMessage) ==
static_cast<uint32_t>(StorageManagerIF::ILLEGAL_STORAGE_ID));
REQUIRE(ActionMessage::getStep(&testMessage) == 0);
}
SECTION("Data Reply") {}
}

View File

@ -0,0 +1,49 @@
#ifndef UNITTEST_HOSTED_TESTACTIONHELPER_H_
#define UNITTEST_HOSTED_TESTACTIONHELPER_H_
#include <fsfw/action/HasActionsIF.h>
#include <fsfw/ipc/MessageQueueIF.h>
#include <cstring>
#include "fsfw_tests/unit/CatchDefinitions.h"
class ActionHelperOwnerMockBase : public HasActionsIF {
public:
bool getCommandQueueCalled = false;
bool executeActionCalled = false;
static const size_t MAX_SIZE = 3;
uint8_t buffer[MAX_SIZE] = {0, 0, 0};
size_t size = 0;
MessageQueueId_t getCommandQueue() const override { return tconst::testQueueId; }
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size) override {
executeActionCalled = true;
if (size > MAX_SIZE) {
return 0xAFFE;
}
this->size = size;
memcpy(buffer, data, size);
return HasReturnvaluesIF::RETURN_OK;
}
void clearBuffer() {
this->size = 0;
for (size_t i = 0; i < MAX_SIZE; i++) {
buffer[i] = 0;
}
}
void getBuffer(const uint8_t** ptr, size_t* size) {
if (size != nullptr) {
*size = this->size;
}
if (ptr != nullptr) {
*ptr = buffer;
}
}
};
#endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */

View File

@ -0,0 +1,12 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
testCfdp.cpp
testTlvsLvs.cpp
testAckPdu.cpp
testEofPdu.cpp
testNakPdu.cpp
testFinishedPdu.cpp
testPromptPdu.cpp
testKeepAlivePdu.cpp
testMetadataPdu.cpp
testFileData.cpp
)

View File

@ -0,0 +1,100 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/AckPduDeserializer.h"
#include "fsfw/cfdp/pdu/AckPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("ACK PDU", "[AckPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> buf = {};
uint8_t* bufptr = buf.data();
size_t maxsz = buf.size();
size_t sz = 0;
auto seqNum = TransactionSeqNum(WidthInBytes::TWO_BYTES, 15);
auto sourceId = EntityId(WidthInBytes::TWO_BYTES, 1);
auto destId = EntityId(WidthInBytes::TWO_BYTES, 2);
auto pduConf = PduConfig(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
AckInfo ackInfo(FileDirectives::EOF_DIRECTIVE, ConditionCode::NO_ERROR,
AckTransactionStatus::ACTIVE);
auto ackSerializer = AckPduSerializer(ackInfo, pduConf);
result = ackSerializer.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
SECTION("Serialize") {
REQUIRE(buf.data()[sz - 3] == cfdp::FileDirectives::ACK);
REQUIRE((buf.data()[sz - 2] >> 4) == FileDirectives::EOF_DIRECTIVE);
REQUIRE((buf.data()[sz - 2] & 0x0f) == 0);
REQUIRE(buf.data()[sz - 1] == AckTransactionStatus::ACTIVE);
ackInfo.setAckedDirective(FileDirectives::FINISH);
ackInfo.setAckedConditionCode(ConditionCode::FILESTORE_REJECTION);
ackInfo.setTransactionStatus(AckTransactionStatus::TERMINATED);
auto ackSerializer2 = AckPduSerializer(ackInfo, pduConf);
bufptr = buf.data();
sz = 0;
result = ackSerializer2.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(buf.data()[sz - 3] == cfdp::FileDirectives::ACK);
REQUIRE((buf.data()[sz - 2] >> 4) == FileDirectives::FINISH);
REQUIRE((buf.data()[sz - 2] & 0x0f) == 0b0001);
REQUIRE((buf.data()[sz - 1] >> 4) == ConditionCode::FILESTORE_REJECTION);
REQUIRE((buf.data()[sz - 1] & 0b11) == AckTransactionStatus::TERMINATED);
bufptr = buf.data();
sz = 0;
ackInfo.setAckedDirective(FileDirectives::KEEP_ALIVE);
auto ackSerializer3 = AckPduSerializer(ackInfo, pduConf);
result = ackSerializer3.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
// Invalid file directive
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
ackInfo.setAckedDirective(FileDirectives::FINISH);
// buffer too small
result = ackSerializer.serialize(&bufptr, &sz, 8, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
SECTION("Deserialize") {
AckInfo ackInfo;
auto reader = AckPduDeserializer(buf.data(), sz, ackInfo);
result = reader.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(ackInfo.getAckedDirective() == FileDirectives::EOF_DIRECTIVE);
REQUIRE(ackInfo.getAckedConditionCode() == ConditionCode::NO_ERROR);
REQUIRE(ackInfo.getDirectiveSubtypeCode() == 0);
REQUIRE(ackInfo.getTransactionStatus() == AckTransactionStatus::ACTIVE);
AckInfo newInfo = AckInfo(FileDirectives::FINISH, ConditionCode::FILESTORE_REJECTION,
AckTransactionStatus::TERMINATED);
auto ackSerializer2 = AckPduSerializer(newInfo, pduConf);
bufptr = buf.data();
sz = 0;
result = ackSerializer2.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
auto reader2 = AckPduDeserializer(buf.data(), sz, ackInfo);
result = reader2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(ackInfo.getAckedDirective() == FileDirectives::FINISH);
REQUIRE(ackInfo.getAckedConditionCode() == ConditionCode::FILESTORE_REJECTION);
REQUIRE(ackInfo.getDirectiveSubtypeCode() == 0b0001);
REQUIRE(ackInfo.getTransactionStatus() == AckTransactionStatus::TERMINATED);
uint8_t prevVal = buf[sz - 2];
buf[sz - 2] = FileDirectives::INVALID_DIRECTIVE << 4;
result = reader2.parseData();
REQUIRE(result == cfdp::INVALID_ACK_DIRECTIVE_FIELDS);
buf[sz - 2] = FileDirectives::FINISH << 4 | 0b1111;
result = reader2.parseData();
REQUIRE(result == cfdp::INVALID_ACK_DIRECTIVE_FIELDS);
buf[sz - 2] = prevVal;
buf[sz - 3] = cfdp::FileDirectives::INVALID_DIRECTIVE;
result = reader2.parseData();
REQUIRE(result == cfdp::INVALID_DIRECTIVE_FIELDS);
buf[sz - 3] = cfdp::FileDirectives::ACK;
auto maxSizeTooSmall = AckPduDeserializer(buf.data(), sz - 2, ackInfo);
result = maxSizeTooSmall.parseData();
REQUIRE(result == SerializeIF::STREAM_TOO_SHORT);
}
}

View File

@ -0,0 +1,368 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include <cstring>
#include "fsfw/cfdp/FileSize.h"
#include "fsfw/cfdp/pdu/FileDirectiveDeserializer.h"
#include "fsfw/cfdp/pdu/FileDirectiveSerializer.h"
#include "fsfw/cfdp/pdu/HeaderDeserializer.h"
#include "fsfw/cfdp/pdu/HeaderSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/serialize/SerializeAdapter.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("CFDP Base", "[CfdpBase]") {
using namespace cfdp;
std::array<uint8_t, 32> serBuf;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
cfdp::TransactionSeqNum seqNum = TransactionSeqNum(cfdp::WidthInBytes::ONE_BYTE, 2);
cfdp::EntityId sourceId = EntityId(cfdp::WidthInBytes::ONE_BYTE, 0);
cfdp::EntityId destId = EntityId(cfdp::WidthInBytes::ONE_BYTE, 1);
PduConfig pduConf =
PduConfig(cfdp::TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId, false);
uint8_t* serTarget = serBuf.data();
const uint8_t* deserTarget = serTarget;
size_t serSize = 0;
SECTION("Header Serialization") {
auto headerSerializer = HeaderSerializer(pduConf, cfdp::PduType::FILE_DIRECTIVE, 0);
const uint8_t** dummyPtr = nullptr;
ReturnValue_t deserResult =
headerSerializer.deSerialize(dummyPtr, &serSize, SerializeIF::Endianness::NETWORK);
REQUIRE(deserResult == retval::CATCH_FAILED);
deserResult = headerSerializer.serialize(nullptr, &serSize, serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(deserResult == retval::CATCH_FAILED);
REQUIRE(seqNum.getSerializedSize() == 1);
REQUIRE(headerSerializer.getPduDataFieldLen() == 0);
REQUIRE(headerSerializer.getSerializedSize() == 7);
REQUIRE(headerSerializer.getWholePduSize() == 7);
REQUIRE(headerSerializer.getCrcFlag() == false);
REQUIRE(headerSerializer.getDirection() == cfdp::Direction::TOWARDS_RECEIVER);
REQUIRE(headerSerializer.getLargeFileFlag() == false);
REQUIRE(headerSerializer.getLenEntityIds() == 1);
REQUIRE(headerSerializer.getLenSeqNum() == 1);
REQUIRE(headerSerializer.getPduType() == cfdp::PduType::FILE_DIRECTIVE);
REQUIRE(headerSerializer.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
REQUIRE(headerSerializer.getSegmentationControl() == false);
REQUIRE(headerSerializer.getTransmissionMode() == cfdp::TransmissionModes::ACKNOWLEDGED);
cfdp::TransactionSeqNum seqNumLocal;
headerSerializer.getTransactionSeqNum(seqNumLocal);
REQUIRE(seqNumLocal.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(seqNumLocal.getValue() == 2);
cfdp::EntityId sourceDestId;
headerSerializer.getSourceId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(sourceDestId.getValue() == 0);
headerSerializer.getDestId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(sourceDestId.getValue() == 1);
result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(serSize == 7);
// Only version bits are set
REQUIRE(serBuf[0] == 0b00100000);
// PDU data field length is 0
REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 0);
// Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001);
// Source ID
REQUIRE(serBuf[4] == 0);
// Transaction Seq Number
REQUIRE(serBuf[5] == 2);
// Dest ID
REQUIRE(serBuf[6] == 1);
for (uint8_t idx = 0; idx < 7; idx++) {
ReturnValue_t result =
headerSerializer.serialize(&serTarget, &serSize, idx, SerializeIF::Endianness::BIG);
REQUIRE(result == static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
}
// Set PDU data field len
headerSerializer.setPduDataFieldLen(0x0ff0);
REQUIRE(headerSerializer.getPduDataFieldLen() == 0x0ff0);
REQUIRE(headerSerializer.getSerializedSize() == 7);
REQUIRE(headerSerializer.getWholePduSize() == 7 + 0x0ff0);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
REQUIRE(serBuf[1] == 0x0f);
REQUIRE(serBuf[2] == 0xf0);
pduConf.crcFlag = true;
pduConf.largeFile = true;
pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
pduConf.mode = cfdp::TransmissionModes::UNACKNOWLEDGED;
headerSerializer.setSegmentationControl(
cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
headerSerializer.setPduType(cfdp::PduType::FILE_DATA);
headerSerializer.setSegmentMetadataFlag(cfdp::SegmentMetadataFlag::PRESENT);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
// Everything except version bit flipped to one now
REQUIRE(serBuf[0] == 0x3f);
REQUIRE(serBuf[3] == 0x99);
pduConf.seqNum.setValue(cfdp::WidthInBytes::TWO_BYTES, 0x0fff);
pduConf.sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0xff00ff00);
pduConf.destId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0x00ff00ff);
REQUIRE(pduConf.sourceId.getSerializedSize() == 4);
REQUIRE(headerSerializer.getSerializedSize() == 14);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
for (uint8_t idx = 0; idx < 14; idx++) {
ReturnValue_t result =
headerSerializer.serialize(&serTarget, &serSize, idx, SerializeIF::Endianness::BIG);
REQUIRE(result == static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
}
REQUIRE(headerSerializer.getCrcFlag() == true);
REQUIRE(headerSerializer.getDirection() == cfdp::Direction::TOWARDS_SENDER);
REQUIRE(headerSerializer.getLargeFileFlag() == true);
REQUIRE(headerSerializer.getLenEntityIds() == 4);
REQUIRE(headerSerializer.getLenSeqNum() == 2);
REQUIRE(headerSerializer.getPduType() == cfdp::PduType::FILE_DATA);
REQUIRE(headerSerializer.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::PRESENT);
REQUIRE(headerSerializer.getTransmissionMode() == cfdp::TransmissionModes::UNACKNOWLEDGED);
REQUIRE(headerSerializer.getSegmentationControl() == true);
// Last three bits are 2 now (length of seq number) and bit 1 to bit 3 is 4 (len entity IDs)
REQUIRE(serBuf[3] == 0b11001010);
uint32_t entityId = 0;
size_t deSerSize = 0;
SerializeAdapter::deSerialize(&entityId, serBuf.data() + 4, &deSerSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deSerSize == 4);
REQUIRE(entityId == 0xff00ff00);
uint16_t seqNum = 0;
SerializeAdapter::deSerialize(&seqNum, serBuf.data() + 8, &deSerSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deSerSize == 2);
REQUIRE(seqNum == 0x0fff);
SerializeAdapter::deSerialize(&entityId, serBuf.data() + 10, &deSerSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deSerSize == 4);
REQUIRE(entityId == 0x00ff00ff);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::ONE_BYTE, 0xfff);
REQUIRE(result == retval::CATCH_FAILED);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::TWO_BYTES, 0xfffff);
REQUIRE(result == retval::CATCH_FAILED);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0xfffffffff);
REQUIRE(result == retval::CATCH_FAILED);
uint8_t oneByteSourceId = 32;
serTarget = &oneByteSourceId;
size_t deserLen = 1;
pduConf.sourceId.deSerialize(cfdp::WidthInBytes::ONE_BYTE,
const_cast<const uint8_t**>(&serTarget), &deserLen,
SerializeIF::Endianness::MACHINE);
REQUIRE(pduConf.sourceId.getValue() == 32);
uint16_t twoByteSourceId = 0xf0f0;
serTarget = reinterpret_cast<uint8_t*>(&twoByteSourceId);
deserLen = 2;
pduConf.sourceId.deSerialize(cfdp::WidthInBytes::TWO_BYTES,
const_cast<const uint8_t**>(&serTarget), &deserLen,
SerializeIF::Endianness::MACHINE);
REQUIRE(pduConf.sourceId.getValue() == 0xf0f0);
uint32_t fourByteSourceId = 0xf0f0f0f0;
serTarget = reinterpret_cast<uint8_t*>(&fourByteSourceId);
deserLen = 4;
pduConf.sourceId.deSerialize(cfdp::WidthInBytes::FOUR_BYTES,
const_cast<const uint8_t**>(&serTarget), &deserLen,
SerializeIF::Endianness::MACHINE);
REQUIRE(pduConf.sourceId.getValue() == 0xf0f0f0f0);
pduConf.sourceId.setValue(cfdp::WidthInBytes::ONE_BYTE, 1);
serTarget = serBuf.data();
serSize = 1;
result = pduConf.sourceId.serialize(&serTarget, &serSize, 1, SerializeIF::Endianness::MACHINE);
REQUIRE(result == static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
}
SECTION("Header Deserialization") {
// We unittested the serializer before, so we can use it now to generate valid raw CFDP
// data
auto headerSerializer = HeaderSerializer(pduConf, cfdp::PduType::FILE_DIRECTIVE, 0);
ReturnValue_t result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 0);
// Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001);
REQUIRE(serSize == 7);
// Deser call not strictly necessary
auto headerDeser = HeaderDeserializer(serBuf.data(), serBuf.size());
ReturnValue_t serResult = headerDeser.parseData();
REQUIRE(serResult == retval::CATCH_OK);
REQUIRE(headerDeser.getPduDataFieldLen() == 0);
REQUIRE(headerDeser.getHeaderSize() == 7);
REQUIRE(headerDeser.getWholePduSize() == 7);
REQUIRE(headerDeser.getCrcFlag() == false);
REQUIRE(headerDeser.getDirection() == cfdp::Direction::TOWARDS_RECEIVER);
REQUIRE(headerDeser.getLargeFileFlag() == false);
REQUIRE(headerDeser.getLenEntityIds() == 1);
REQUIRE(headerDeser.getLenSeqNum() == 1);
REQUIRE(headerDeser.getPduType() == cfdp::PduType::FILE_DIRECTIVE);
REQUIRE(headerDeser.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
REQUIRE(headerDeser.getSegmentationControl() == false);
REQUIRE(headerDeser.getTransmissionMode() == cfdp::TransmissionModes::ACKNOWLEDGED);
pduConf.crcFlag = true;
pduConf.largeFile = true;
pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
pduConf.mode = cfdp::TransmissionModes::UNACKNOWLEDGED;
headerSerializer.setSegmentationControl(
cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
headerSerializer.setPduType(cfdp::PduType::FILE_DATA);
headerSerializer.setSegmentMetadataFlag(cfdp::SegmentMetadataFlag::PRESENT);
result = pduConf.seqNum.setValue(cfdp::WidthInBytes::TWO_BYTES, 0x0fff);
REQUIRE(result == retval::CATCH_OK);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0xff00ff00);
REQUIRE(result == retval::CATCH_OK);
result = pduConf.destId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0x00ff00ff);
REQUIRE(result == retval::CATCH_OK);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
headerDeser = HeaderDeserializer(serBuf.data(), serBuf.size());
result = headerDeser.parseData();
REQUIRE(result == retval::CATCH_OK);
// Everything except version bit flipped to one now
REQUIRE(serBuf[0] == 0x3f);
REQUIRE(serBuf[3] == 0b11001010);
REQUIRE(headerDeser.getWholePduSize() == 14);
REQUIRE(headerDeser.getCrcFlag() == true);
REQUIRE(headerDeser.getDirection() == cfdp::Direction::TOWARDS_SENDER);
REQUIRE(headerDeser.getLargeFileFlag() == true);
REQUIRE(headerDeser.getLenEntityIds() == 4);
REQUIRE(headerDeser.getLenSeqNum() == 2);
REQUIRE(headerDeser.getPduType() == cfdp::PduType::FILE_DATA);
REQUIRE(headerDeser.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::PRESENT);
REQUIRE(headerDeser.getSegmentationControl() == true);
REQUIRE(headerDeser.getTransmissionMode() == cfdp::TransmissionModes::UNACKNOWLEDGED);
cfdp::TransactionSeqNum seqNumLocal;
headerDeser.getTransactionSeqNum(seqNumLocal);
REQUIRE(seqNumLocal.getWidth() == cfdp::WidthInBytes::TWO_BYTES);
REQUIRE(seqNumLocal.getValue() == 0x0fff);
cfdp::EntityId sourceDestId;
headerDeser.getSourceId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::FOUR_BYTES);
REQUIRE(sourceDestId.getValue() == 0xff00ff00);
headerDeser.getDestId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::FOUR_BYTES);
REQUIRE(sourceDestId.getValue() == 0x00ff00ff);
size_t deSerSize = headerDeser.getWholePduSize();
serTarget = serBuf.data();
const uint8_t** serTargetConst = const_cast<const uint8_t**>(&serTarget);
result = headerDeser.parseData();
REQUIRE(result == retval::CATCH_OK);
headerDeser.setData(nullptr, -1);
REQUIRE(headerDeser.getHeaderSize() == 0);
headerDeser.setData(serBuf.data(), serBuf.size());
serTarget = serBuf.data();
serSize = 0;
pduConf.sourceId.setValue(cfdp::WidthInBytes::ONE_BYTE, 22);
pduConf.destId.setValue(cfdp::WidthInBytes::ONE_BYTE, 48);
result = headerSerializer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::BIG);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(headerDeser.getWholePduSize() == 8);
headerDeser.setData(serBuf.data(), serBuf.size());
headerDeser.getSourceId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(sourceDestId.getValue() == 22);
}
SECTION("File Directive") {
auto fdSer = FileDirectiveSerializer(pduConf, FileDirectives::ACK, 4);
REQUIRE(fdSer.getSerializedSize() == 8);
serTarget = serBuf.data();
serSize = 0;
result = fdSer.serialize(&serTarget, &serSize, serBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Only version bits are set
REQUIRE(serBuf[0] == 0b00100000);
// PDU data field length is 5 (4 + Directive code octet)
REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 5);
// Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001);
// Source ID
REQUIRE(serBuf[4] == 0);
// Transaction Seq Number
REQUIRE(serBuf[5] == 2);
// Dest ID
REQUIRE(serBuf[6] == 1);
REQUIRE(serBuf[7] == FileDirectives::ACK);
serTarget = serBuf.data();
size_t deserSize = 20;
serSize = 0;
REQUIRE(fdSer.deSerialize(&deserTarget, &deserSize, SerializeIF::Endianness::NETWORK) ==
HasReturnvaluesIF::RETURN_FAILED);
REQUIRE(fdSer.serialize(nullptr, nullptr, 85, SerializeIF::Endianness::NETWORK) ==
HasReturnvaluesIF::RETURN_FAILED);
for (uint8_t idx = 0; idx < 8; idx++) {
serTarget = serBuf.data();
serSize = 0;
REQUIRE(fdSer.serialize(&serTarget, &serSize, idx, SerializeIF::Endianness::NETWORK) ==
SerializeIF::BUFFER_TOO_SHORT);
}
deserTarget = serBuf.data();
deserSize = 0;
auto fdDeser = FileDirectiveDeserializer(deserTarget, serBuf.size());
REQUIRE(fdDeser.getEndianness() == SerializeIF::Endianness::NETWORK);
fdDeser.setEndianness(SerializeIF::Endianness::MACHINE);
REQUIRE(fdDeser.getEndianness() == SerializeIF::Endianness::MACHINE);
fdDeser.setEndianness(SerializeIF::Endianness::NETWORK);
REQUIRE(fdDeser.parseData() == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fdDeser.getFileDirective() == FileDirectives::ACK);
REQUIRE(fdDeser.getPduDataFieldLen() == 5);
REQUIRE(fdDeser.getHeaderSize() == 8);
REQUIRE(fdDeser.getPduType() == cfdp::PduType::FILE_DIRECTIVE);
serBuf[7] = 0xff;
// Invalid file directive
REQUIRE(fdDeser.parseData() == cfdp::INVALID_DIRECTIVE_FIELDS);
}
SECTION("FileSize") {
std::array<uint8_t, 8> fssBuf = {};
uint8_t* buffer = fssBuf.data();
size_t size = 0;
cfdp::FileSize fss;
REQUIRE(fss.getSize() == 0);
fss.setFileSize(0x20, false);
ReturnValue_t result =
fss.serialize(&buffer, &size, fssBuf.size(), SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
uint32_t fileSize = 0;
result = SerializeAdapter::deSerialize(&fileSize, fssBuf.data(), nullptr,
SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fileSize == 0x20);
}
}

View File

@ -0,0 +1,118 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/EofPduDeserializer.h"
#include "fsfw/cfdp/pdu/EofPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("EOF PDU", "[EofPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 128> buf = {};
uint8_t* bufPtr = buf.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
EntityIdTlv faultLoc(destId);
FileSize fileSize(12);
// We can already set the fault location, it will be ignored
EofInfo eofInfo(cfdp::ConditionCode::NO_ERROR, 5, fileSize, &faultLoc);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
auto eofSerializer = EofPduSerializer(pduConf, eofInfo);
SECTION("Serialize") {
result = eofSerializer.serialize(&bufPtr, &sz, buf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(((buf[1] << 8) | buf[2]) == 10);
uint32_t checksum = 0;
result = SerializeAdapter::deSerialize(&checksum, buf.data() + sz - 8, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(checksum == 5);
uint32_t fileSizeVal = 0;
result = SerializeAdapter::deSerialize(&fileSizeVal, buf.data() + sz - 4, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fileSizeVal == 12);
REQUIRE(buf[sz - 10] == cfdp::FileDirectives::EOF_DIRECTIVE);
REQUIRE(buf[sz - 9] == 0x00);
REQUIRE(sz == 20);
eofInfo.setConditionCode(cfdp::ConditionCode::FILESTORE_REJECTION);
eofInfo.setFileSize(0x10ffffff10, true);
pduConf.largeFile = true;
// Should serialize with fault location now
auto serializeWithFaultLocation = EofPduSerializer(pduConf, eofInfo);
bufPtr = buf.data();
sz = 0;
result = serializeWithFaultLocation.serialize(&bufPtr, &sz, buf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(sz == 28);
REQUIRE(buf[10] == cfdp::FileDirectives::EOF_DIRECTIVE);
REQUIRE(buf[11] >> 4 == cfdp::ConditionCode::FILESTORE_REJECTION);
uint64_t fileSizeLarge = 0;
result = SerializeAdapter::deSerialize(&fileSizeLarge, buf.data() + 16, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(fileSizeLarge == 0x10ffffff10);
REQUIRE(buf[sz - 4] == cfdp::TlvTypes::ENTITY_ID);
// width of entity ID is 2
REQUIRE(buf[sz - 3] == 2);
uint16_t entityIdRaw = 0;
result = SerializeAdapter::deSerialize(&entityIdRaw, buf.data() + sz - 2, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(entityIdRaw == 2);
bufPtr = buf.data();
sz = 0;
for (size_t idx = 0; idx < 27; idx++) {
result =
serializeWithFaultLocation.serialize(&bufPtr, &sz, idx, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
bufPtr = buf.data();
sz = 0;
}
eofInfo.setChecksum(16);
eofInfo.setFaultLoc(nullptr);
}
SECTION("Deserialize") {
result = eofSerializer.serialize(&bufPtr, &sz, buf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
EntityIdTlv tlv(destId);
EofInfo emptyInfo(&tlv);
auto deserializer = EofPduDeserializer(buf.data(), buf.size(), emptyInfo);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getConditionCode() == cfdp::ConditionCode::NO_ERROR);
REQUIRE(emptyInfo.getChecksum() == 5);
REQUIRE(emptyInfo.getFileSize().getSize() == 12);
eofInfo.setConditionCode(cfdp::ConditionCode::FILESTORE_REJECTION);
eofInfo.setFileSize(0x10ffffff10, true);
pduConf.largeFile = true;
// Should serialize with fault location now
auto serializeWithFaultLocation = EofPduSerializer(pduConf, eofInfo);
bufPtr = buf.data();
sz = 0;
result = serializeWithFaultLocation.serialize(&bufPtr, &sz, buf.size(),
SerializeIF::Endianness::NETWORK);
auto deserializer2 = EofPduDeserializer(buf.data(), buf.size(), emptyInfo);
result = deserializer2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getConditionCode() == cfdp::ConditionCode::FILESTORE_REJECTION);
REQUIRE(emptyInfo.getChecksum() == 5);
REQUIRE(emptyInfo.getFileSize().getSize() == 0x10ffffff10);
REQUIRE(emptyInfo.getFaultLoc()->getType() == cfdp::TlvTypes::ENTITY_ID);
REQUIRE(emptyInfo.getFaultLoc()->getSerializedSize() == 4);
uint16_t destId = emptyInfo.getFaultLoc()->getEntityId().getValue();
REQUIRE(destId == 2);
for (size_t maxSz = 0; maxSz < deserializer2.getWholePduSize() - 1; maxSz++) {
auto invalidDeser = EofPduDeserializer(buf.data(), maxSz, emptyInfo);
result = invalidDeser.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,171 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/FileDataDeserializer.h"
#include "fsfw/cfdp/pdu/FileDataSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/serviceinterface.h"
TEST_CASE("File Data PDU", "[FileDataPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 128> fileBuffer = {};
std::array<uint8_t, 256> fileDataBuffer = {};
uint8_t* buffer = fileDataBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
for (uint8_t idx = 0; idx < 10; idx++) {
fileBuffer[idx] = idx;
}
FileSize offset(50);
FileDataInfo info(offset, fileBuffer.data(), 10);
SECTION("Serialization") {
FileDataSerializer serializer(pduConf, info);
result =
serializer.serialize(&buffer, &sz, fileDataBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(sz == 24);
// 10 file bytes plus 4 byte offset
REQUIRE(((fileDataBuffer[1] << 8) | fileDataBuffer[2]) == 14);
// File Data -> Fourth bit is one
REQUIRE(fileDataBuffer[0] == 0b00110000);
uint32_t offsetRaw = 0;
buffer = fileDataBuffer.data();
result = SerializeAdapter::deSerialize(&offsetRaw, buffer + 10, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(offsetRaw == 50);
buffer = fileDataBuffer.data() + 14;
for (size_t idx = 0; idx < 10; idx++) {
REQUIRE(buffer[idx] == idx);
}
REQUIRE(info.hasSegmentMetadata() == false);
info.addSegmentMetadataInfo(cfdp::RecordContinuationState::CONTAINS_START_AND_END,
fileBuffer.data(), 10);
REQUIRE(info.hasSegmentMetadata() == true);
REQUIRE(info.getSegmentationControl() ==
cfdp::SegmentationControl::NO_RECORD_BOUNDARIES_PRESERVATION);
info.setSegmentationControl(cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
serializer.update();
REQUIRE(serializer.getSegmentationControl() ==
cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
buffer = fileDataBuffer.data();
sz = 0;
serializer.setSegmentationControl(cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
result =
serializer.serialize(&buffer, &sz, fileDataBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(((fileDataBuffer[1] << 8) | fileDataBuffer[2]) == 25);
// First bit: Seg Ctrl is set
// Bits 1 to 3 length of enitity IDs is 2
// Bit 4: Segment metadata flag is set
// Bit 5 to seven: length of transaction seq num is 2
REQUIRE(fileDataBuffer[3] == 0b10101010);
REQUIRE((fileDataBuffer[10] >> 6) &
0b11 == cfdp::RecordContinuationState::CONTAINS_START_AND_END);
// Segment metadata length
REQUIRE((fileDataBuffer[10] & 0x3f) == 10);
buffer = fileDataBuffer.data() + 11;
// Check segment metadata
for (size_t idx = 0; idx < 10; idx++) {
REQUIRE(buffer[idx] == idx);
}
// Check filedata
buffer = fileDataBuffer.data() + 25;
for (size_t idx = 0; idx < 10; idx++) {
REQUIRE(buffer[idx] == idx);
}
for (size_t invalidStartSz = 1; invalidStartSz < sz; invalidStartSz++) {
buffer = fileDataBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &invalidStartSz, sz, SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
info.setSegmentMetadataFlag(true);
REQUIRE(info.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::PRESENT);
info.setSegmentMetadataFlag(false);
REQUIRE(info.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
info.setRecordContinuationState(cfdp::RecordContinuationState::CONTAINS_END_NO_START);
info.setSegmentMetadataLen(10);
info.setSegmentMetadata(nullptr);
info.setFileData(nullptr, 0);
}
SECTION("Deserialization") {
FileDataSerializer serializer(pduConf, info);
result =
serializer.serialize(&buffer, &sz, fileDataBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FileSize emptyOffset;
FileDataInfo emptyInfo(emptyOffset);
FileDataDeserializer deserializer(fileDataBuffer.data(), fileDataBuffer.size(), emptyInfo);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserializer.getWholePduSize() == 24);
REQUIRE(deserializer.getPduDataFieldLen() == 14);
REQUIRE(deserializer.getSegmentationControl() ==
cfdp::SegmentationControl::NO_RECORD_BOUNDARIES_PRESERVATION);
REQUIRE(deserializer.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
REQUIRE(emptyInfo.getOffset().getSize() == 50);
REQUIRE(emptyInfo.hasSegmentMetadata() == false);
size_t emptyFileSize = 0;
const uint8_t* fileData = emptyInfo.getFileData(&emptyFileSize);
REQUIRE(emptyFileSize == 10);
for (size_t idx = 0; idx < 10; idx++) {
REQUIRE(fileData[idx] == idx);
}
deserializer.setEndianness(SerializeIF::Endianness::NETWORK);
info.addSegmentMetadataInfo(cfdp::RecordContinuationState::CONTAINS_START_AND_END,
fileBuffer.data(), 10);
serializer.update();
buffer = fileDataBuffer.data();
sz = 0;
result =
serializer.serialize(&buffer, &sz, fileDataBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getOffset().getSize() == 50);
REQUIRE(emptyInfo.hasSegmentMetadata() == true);
REQUIRE(emptyInfo.getRecordContinuationState() ==
cfdp::RecordContinuationState::CONTAINS_START_AND_END);
emptyFileSize = 0;
fileData = emptyInfo.getFileData(&emptyFileSize);
REQUIRE(emptyFileSize == 10);
for (size_t idx = 0; idx < 10; idx++) {
REQUIRE(fileData[idx] == idx);
}
size_t segmentMetadataLen = 0;
fileData = emptyInfo.getSegmentMetadata(&segmentMetadataLen);
REQUIRE(segmentMetadataLen == 10);
for (size_t idx = 0; idx < 10; idx++) {
REQUIRE(fileData[idx] == idx);
}
for (size_t invalidPduField = 0; invalidPduField < 24; invalidPduField++) {
fileDataBuffer[1] = (invalidPduField >> 8) & 0xff;
fileDataBuffer[2] = invalidPduField & 0xff;
result = deserializer.parseData();
// Starting at 15, the file data is parsed. There is not leading file data length
// field to the parser can't check whether the remaining length is valid
if (invalidPduField < 15) {
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}
}

View File

@ -0,0 +1,189 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/FinishedPduDeserializer.h"
#include "fsfw/cfdp/pdu/FinishedPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("Finished PDU", "[FinishedPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> fnBuffer = {};
uint8_t* buffer = fnBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
cfdp::Lv emptyFsMsg;
FinishedInfo info(cfdp::ConditionCode::INACTIVITY_DETECTED,
cfdp::FinishedDeliveryCode::DATA_INCOMPLETE,
cfdp::FinishedFileStatus::DISCARDED_DELIBERATELY);
SECTION("Serialize") {
FinishPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, fnBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 12);
REQUIRE(((fnBuffer[1] << 8) | fnBuffer[2]) == 2);
REQUIRE(fnBuffer[10] == cfdp::FileDirectives::FINISH);
REQUIRE(((fnBuffer[sz - 1] >> 4) & 0x0f) == cfdp::ConditionCode::INACTIVITY_DETECTED);
REQUIRE(((fnBuffer[sz - 1] >> 2) & 0x01) == cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
REQUIRE((fnBuffer[sz - 1] & 0b11) == cfdp::FinishedFileStatus::DISCARDED_DELIBERATELY);
REQUIRE(sz == 12);
// Add a filestore response
std::string firstName = "hello.txt";
cfdp::Lv firstNameLv(reinterpret_cast<const uint8_t*>(firstName.data()), firstName.size());
FilestoreResponseTlv response(cfdp::FilestoreActionCode::DELETE_FILE,
cfdp::FSR_APPEND_FILE_1_NOT_EXISTS, firstNameLv, nullptr);
FilestoreResponseTlv* responsePtr = &response;
REQUIRE(response.getSerializedSize() == 14);
size_t len = 1;
info.setFilestoreResponsesArray(&responsePtr, &len, &len);
serializer.updateDirectiveFieldLen();
REQUIRE(serializer.getSerializedSize() == 12 + 14);
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, fnBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 12 + 14);
REQUIRE(serializer.getPduDataFieldLen() == 16);
// Add two filestore responses and a fault location parameter
std::string secondName = "hello2.txt";
cfdp::Lv secondNameLv(reinterpret_cast<const uint8_t*>(secondName.data()), secondName.size());
FilestoreResponseTlv response2(cfdp::FilestoreActionCode::DENY_FILE, cfdp::FSR_SUCCESS,
secondNameLv, nullptr);
REQUIRE(response2.getSerializedSize() == 15);
len = 2;
std::array<FilestoreResponseTlv*, 2> responses{&response, &response2};
info.setFilestoreResponsesArray(responses.data(), &len, &len);
serializer.updateDirectiveFieldLen();
EntityIdTlv faultLoc(destId);
REQUIRE(faultLoc.getSerializedSize() == 4);
info.setFaultLocation(&faultLoc);
serializer.updateDirectiveFieldLen();
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, fnBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
info.setConditionCode(cfdp::ConditionCode::FILESTORE_REJECTION);
REQUIRE(serializer.getSerializedSize() == 12 + 14 + 15 + 4);
REQUIRE(sz == 12 + 14 + 15 + 4);
info.setFileStatus(cfdp::FinishedFileStatus::DISCARDED_FILESTORE_REJECTION);
REQUIRE(info.getFileStatus() == cfdp::FinishedFileStatus::DISCARDED_FILESTORE_REJECTION);
info.setDeliveryCode(cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
REQUIRE(info.getDeliveryCode() == cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
for (size_t maxSz = 0; maxSz < 45; maxSz++) {
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, maxSz, SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
SECTION("Deserialize") {
FinishedInfo emptyInfo;
FinishPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, fnBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FinishPduDeserializer deserializer(fnBuffer.data(), fnBuffer.size(), emptyInfo);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getFileStatus() == cfdp::FinishedFileStatus::DISCARDED_DELIBERATELY);
REQUIRE(emptyInfo.getConditionCode() == cfdp::ConditionCode::INACTIVITY_DETECTED);
REQUIRE(emptyInfo.getDeliveryCode() == cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
// Add a filestore response
sz = 0;
buffer = fnBuffer.data();
std::string firstName = "hello.txt";
cfdp::Lv firstNameLv(reinterpret_cast<const uint8_t*>(firstName.data()), firstName.size());
FilestoreResponseTlv response(cfdp::FilestoreActionCode::DELETE_FILE, cfdp::FSR_NOT_PERFORMED,
firstNameLv, nullptr);
FilestoreResponseTlv* responsePtr = &response;
size_t len = 1;
info.setFilestoreResponsesArray(&responsePtr, &len, &len);
serializer.updateDirectiveFieldLen();
REQUIRE(serializer.getPduDataFieldLen() == 16);
result = serializer.serialize(&buffer, &sz, fnBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FilestoreResponseTlv emptyResponse(firstNameLv, nullptr);
responsePtr = &emptyResponse;
emptyInfo.setFilestoreResponsesArray(&responsePtr, nullptr, &len);
FinishPduDeserializer deserializer2(fnBuffer.data(), fnBuffer.size(), emptyInfo);
result = deserializer2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getFsResponsesLen() == 1);
FilestoreResponseTlv** responseArray = nullptr;
emptyInfo.getFilestoreResonses(&responseArray, nullptr, nullptr);
REQUIRE(responseArray[0]->getActionCode() == cfdp::FilestoreActionCode::DELETE_FILE);
REQUIRE(responseArray[0]->getStatusCode() == cfdp::FSR_NOT_PERFORMED);
auto& fileNameRef = responseArray[0]->getFirstFileName();
size_t stringSize = 0;
const char* string = reinterpret_cast<const char*>(fileNameRef.getValue(&stringSize));
std::string firstFileName(string, stringSize);
REQUIRE(firstFileName == "hello.txt");
// Add two filestore responses and a fault location parameter
std::string secondName = "hello2.txt";
cfdp::Lv secondNameLv(reinterpret_cast<const uint8_t*>(secondName.data()), secondName.size());
FilestoreResponseTlv response2(cfdp::FilestoreActionCode::DENY_FILE, cfdp::FSR_SUCCESS,
secondNameLv, nullptr);
REQUIRE(response2.getSerializedSize() == 15);
len = 2;
std::array<FilestoreResponseTlv*, 2> responses{&response, &response2};
info.setFilestoreResponsesArray(responses.data(), &len, &len);
serializer.updateDirectiveFieldLen();
EntityIdTlv faultLoc(destId);
info.setFaultLocation(&faultLoc);
serializer.updateDirectiveFieldLen();
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, fnBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
EntityId emptyId;
EntityIdTlv emptyFaultLoc(emptyId);
emptyInfo.setFaultLocation(&emptyFaultLoc);
response.setFilestoreMessage(&emptyFsMsg);
emptyInfo.setFilestoreResponsesArray(responses.data(), &len, &len);
response2.setFilestoreMessage(&emptyFsMsg);
FinishPduDeserializer deserializer3(fnBuffer.data(), fnBuffer.size(), emptyInfo);
result = deserializer3.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
auto& infoRef = deserializer3.getInfo();
REQUIRE(deserializer3.getWholePduSize() == 45);
size_t invalidMaxLen = 1;
emptyInfo.setFilestoreResponsesArray(responses.data(), &len, &invalidMaxLen);
result = deserializer3.parseData();
REQUIRE(result == cfdp::FINISHED_CANT_PARSE_FS_RESPONSES);
emptyInfo.setFilestoreResponsesArray(nullptr, nullptr, nullptr);
result = deserializer3.parseData();
REQUIRE(result == cfdp::FINISHED_CANT_PARSE_FS_RESPONSES);
// Clear condition code
auto tmp = fnBuffer[11];
fnBuffer[11] = fnBuffer[11] & ~0xf0;
fnBuffer[11] = fnBuffer[11] | (cfdp::ConditionCode::NO_ERROR << 4);
emptyInfo.setFilestoreResponsesArray(responses.data(), &len, &len);
result = deserializer3.parseData();
REQUIRE(result == cfdp::INVALID_TLV_TYPE);
fnBuffer[11] = tmp;
// Invalid TLV type, should be entity ID
fnBuffer[sz - 4] = cfdp::TlvTypes::FILESTORE_REQUEST;
result = deserializer3.parseData();
REQUIRE(result == cfdp::INVALID_TLV_TYPE);
for (size_t maxSz = 0; maxSz < 45; maxSz++) {
FinishPduDeserializer faultyDeser(fnBuffer.data(), maxSz, emptyInfo);
result = faultyDeser.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,79 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/KeepAlivePduDeserializer.h"
#include "fsfw/cfdp/pdu/KeepAlivePduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("Keep Alive PDU", "[KeepAlivePdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> kaBuffer = {};
uint8_t* buffer = kaBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
FileSize progress(0x50);
SECTION("Serialize") {
KeepAlivePduSerializer serializer(pduConf, progress);
result = serializer.serialize(&buffer, &sz, kaBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(kaBuffer[10] == cfdp::FileDirectives::KEEP_ALIVE);
uint32_t fsRaw = 0;
result = SerializeAdapter::deSerialize(&fsRaw, kaBuffer.data() + 11, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fsRaw == 0x50);
REQUIRE(sz == 15);
REQUIRE(serializer.getWholePduSize() == 15);
REQUIRE(serializer.getPduDataFieldLen() == 5);
pduConf.largeFile = true;
serializer.updateDirectiveFieldLen();
buffer = kaBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &sz, kaBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getWholePduSize() == 19);
REQUIRE(serializer.getPduDataFieldLen() == 9);
uint64_t fsRawLarge = 0;
result = SerializeAdapter::deSerialize(&fsRawLarge, kaBuffer.data() + 11, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fsRawLarge == 0x50);
for (size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
buffer = kaBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &sz, invalidMaxSz, SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
SECTION("Deserialize") {
KeepAlivePduSerializer serializer(pduConf, progress);
result = serializer.serialize(&buffer, &sz, kaBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Set another file size
progress.setFileSize(200, false);
KeepAlivePduDeserializer deserializer(kaBuffer.data(), kaBuffer.size(), progress);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
auto& progRef = deserializer.getProgress();
// Should have been overwritten
REQUIRE(progRef.getSize() == 0x50);
sz = deserializer.getWholePduSize();
// invalid max size
for (size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
deserializer.setData(kaBuffer.data(), invalidMaxSz);
result = deserializer.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,179 @@
#include <fsfw/cfdp/tlv/MessageToUserTlv.h>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/MetadataPduDeserializer.h"
#include "fsfw/cfdp/pdu/MetadataPduSerializer.h"
#include "fsfw/cfdp/tlv/FilestoreResponseTlv.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("Metadata PDU", "[MetadataPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> mdBuffer = {};
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
std::string firstFileName = "hello.txt";
cfdp::Lv sourceFileName(reinterpret_cast<const uint8_t*>(firstFileName.data()),
firstFileName.size());
cfdp::Lv destFileName(nullptr, 0);
FileSize fileSize(35);
MetadataInfo info(false, ChecksumType::MODULAR, fileSize, sourceFileName, destFileName);
FilestoreResponseTlv response(FilestoreActionCode::CREATE_DIRECTORY, FSR_CREATE_NOT_ALLOWED,
sourceFileName, nullptr);
std::array<uint8_t, 3> msg = {0x41, 0x42, 0x43};
cfdp::Tlv responseTlv;
std::array<uint8_t, 64> responseBuf = {};
uint8_t* responseBufPtr = responseBuf.data();
response.convertToTlv(responseTlv, buffer, responseBuf.size(), SerializeIF::Endianness::MACHINE);
MessageToUserTlv msgToUser(msg.data(), msg.size());
std::array<Tlv*, 2> options{&responseTlv, &msgToUser};
REQUIRE(options[0]->getSerializedSize() == 2 + 1 + 10 + 1);
REQUIRE(options[1]->getSerializedSize() == 5);
SECTION("Serialize") {
MetadataPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, mdBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getWholePduSize() == 27);
REQUIRE(info.getSourceFileName().getSerializedSize() == 10);
REQUIRE(info.getDestFileName().getSerializedSize() == 1);
REQUIRE(info.getSerializedSize() == 16);
REQUIRE((mdBuffer[1] << 8 | mdBuffer[2]) == 17);
REQUIRE(mdBuffer[10] == FileDirectives::METADATA);
// no closure requested and checksum type is modular => 0x00
REQUIRE(mdBuffer[11] == 0x00);
uint32_t fileSizeRaw = 0;
result = SerializeAdapter::deSerialize(&fileSizeRaw, mdBuffer.data() + 12, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fileSizeRaw == 35);
REQUIRE(mdBuffer[16] == 9);
REQUIRE(mdBuffer[17] == 'h');
REQUIRE(mdBuffer[18] == 'e');
REQUIRE(mdBuffer[19] == 'l');
REQUIRE(mdBuffer[20] == 'l');
REQUIRE(mdBuffer[21] == 'o');
REQUIRE(mdBuffer[22] == '.');
REQUIRE(mdBuffer[23] == 't');
REQUIRE(mdBuffer[24] == 'x');
REQUIRE(mdBuffer[25] == 't');
REQUIRE(mdBuffer[26] == 0);
std::string otherFileName = "hello2.txt";
cfdp::Lv otherFileNameLv(reinterpret_cast<const uint8_t*>(otherFileName.data()),
otherFileName.size());
info.setSourceFileName(otherFileNameLv);
size_t sizeOfOptions = options.size();
info.setOptionsArray(options.data(), &sizeOfOptions, &sizeOfOptions);
REQUIRE(info.getMaxOptionsLen() == 2);
info.setMaxOptionsLen(3);
REQUIRE(info.getMaxOptionsLen() == 3);
info.setChecksumType(cfdp::ChecksumType::CRC_32C);
info.setClosureRequested(true);
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
result = serializer.serialize(&buffer, &sz, mdBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE((mdBuffer[1] << 8 | mdBuffer[2]) == 37);
auto checksumType = static_cast<cfdp::ChecksumType>(mdBuffer[11] & 0x0f);
REQUIRE(checksumType == cfdp::ChecksumType::CRC_32C);
bool closureRequested = mdBuffer[11] >> 6 & 0x01;
REQUIRE(closureRequested == true);
// The size of the two options is 19. Summing up:
// - 11 bytes of source file name
// - 1 byte for dest file name
// - 4 for FSS
// - 1 leading byte.
// - 1 byte for PDU type
// PDU header has 10 bytes.
// I am not going to check the options raw content, those are part of the dedicated
// TLV unittests
REQUIRE(sz == 10 + 37);
for (size_t maxSz = 0; maxSz < sz; maxSz++) {
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
result = serializer.serialize(&buffer, &sz, maxSz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
for (size_t initSz = 1; initSz < 47; initSz++) {
uint8_t* buffer = mdBuffer.data();
size_t sz = initSz;
result = serializer.serialize(&buffer, &sz, 46, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
info.setDestFileName(destFileName);
}
SECTION("Deserialize") {
MetadataPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, mdBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
MetadataPduDeserializer deserializer(mdBuffer.data(), mdBuffer.size(), info);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
size_t fullSize = deserializer.getWholePduSize();
for (size_t maxSz = 0; maxSz < fullSize; maxSz++) {
MetadataPduDeserializer invalidSzDeser(mdBuffer.data(), maxSz, info);
result = invalidSzDeser.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
size_t sizeOfOptions = options.size();
size_t maxSize = 4;
info.setOptionsArray(options.data(), &sizeOfOptions, &maxSize);
REQUIRE(info.getOptionsLen() == 2);
info.setChecksumType(cfdp::ChecksumType::CRC_32C);
info.setClosureRequested(true);
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
info.setSourceFileName(sourceFileName);
result = serializer.serialize(&buffer, &sz, mdBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
MetadataPduDeserializer deserializer2(mdBuffer.data(), mdBuffer.size(), info);
result = deserializer2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(options[0]->getType() == cfdp::TlvTypes::FILESTORE_RESPONSE);
REQUIRE(options[0]->getSerializedSize() == 14);
REQUIRE(options[1]->getType() == cfdp::TlvTypes::MSG_TO_USER);
REQUIRE(options[1]->getSerializedSize() == 5);
for (size_t invalidFieldLen = 0; invalidFieldLen < 36; invalidFieldLen++) {
mdBuffer[1] = (invalidFieldLen >> 8) & 0xff;
mdBuffer[2] = invalidFieldLen & 0xff;
result = deserializer2.parseData();
if (invalidFieldLen == 17) {
REQUIRE(info.getOptionsLen() == 0);
}
if (invalidFieldLen == 31) {
REQUIRE(info.getOptionsLen() == 1);
}
// This is the precise length where there are no options or one option
if (invalidFieldLen != 17 and invalidFieldLen != 31) {
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
mdBuffer[1] = (36 >> 8) & 0xff;
mdBuffer[2] = 36 & 0xff;
info.setOptionsArray(nullptr, nullptr, nullptr);
REQUIRE(deserializer2.parseData() == cfdp::METADATA_CANT_PARSE_OPTIONS);
info.setOptionsArray(options.data(), &sizeOfOptions, nullptr);
for (size_t maxSz = 0; maxSz < 46; maxSz++) {
MetadataPduDeserializer invalidSzDeser(mdBuffer.data(), maxSz, info);
result = invalidSzDeser.parseData();
REQUIRE(result == SerializeIF::STREAM_TOO_SHORT);
}
}
}

View File

@ -0,0 +1,152 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/NakPduDeserializer.h"
#include "fsfw/cfdp/pdu/NakPduSerializer.h"
#include "fsfw/cfdp/pdu/PduConfig.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("NAK PDU", "[NakPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> nakBuffer = {};
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
FileSize startOfScope(50);
FileSize endOfScope(1050);
NakInfo info(startOfScope, endOfScope);
SECTION("Serializer") {
NakPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, nakBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 19);
REQUIRE(serializer.FileDirectiveSerializer::getSerializedSize() == 11);
REQUIRE(sz == 19);
REQUIRE(serializer.getPduDataFieldLen() == 9);
REQUIRE(((nakBuffer[1] << 8) | nakBuffer[2]) == 0x09);
REQUIRE(nakBuffer[10] == cfdp::FileDirectives::NAK);
uint32_t scope = 0;
result = SerializeAdapter::deSerialize(&scope, nakBuffer.data() + 11, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(scope == 50);
result = SerializeAdapter::deSerialize(&scope, nakBuffer.data() + 15, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(scope == 1050);
NakInfo::SegmentRequest segReq0(cfdp::FileSize(2020), cfdp::FileSize(2520));
NakInfo::SegmentRequest segReq1(cfdp::FileSize(2932), cfdp::FileSize(3021));
// Now add 2 segment requests to NAK info and serialize them as well
std::array<NakInfo::SegmentRequest, 2> segReqs = {segReq0, segReq1};
size_t segReqsLen = segReqs.size();
info.setSegmentRequests(segReqs.data(), &segReqsLen, &segReqsLen);
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
result = serializer.serialize(&buffer, &sz, nakBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 35);
REQUIRE(serializer.getPduDataFieldLen() == 25);
REQUIRE(((nakBuffer[1] << 8) | nakBuffer[2]) == 25);
uint32_t segReqScopes = 0;
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 19, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 2020);
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 23, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 2520);
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 27, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 2932);
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 31, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 3021);
for (size_t maxSz = 0; maxSz < 35; maxSz++) {
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
result = serializer.serialize(&buffer, &sz, maxSz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
for (size_t sz = 35; sz > 0; sz--) {
uint8_t* buffer = nakBuffer.data();
size_t locSize = sz;
result = serializer.serialize(&buffer, &locSize, 35, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
}
SECTION("Deserializer") {
NakPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, nakBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
info.getStartOfScope().setFileSize(0, false);
info.getEndOfScope().setFileSize(0, false);
NakPduDeserializer deserializer(nakBuffer.data(), nakBuffer.size(), info);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserializer.getWholePduSize() == 19);
REQUIRE(info.getStartOfScope().getSize() == 50);
REQUIRE(info.getEndOfScope().getSize() == 1050);
NakInfo::SegmentRequest segReq0(cfdp::FileSize(2020), cfdp::FileSize(2520));
NakInfo::SegmentRequest segReq1(cfdp::FileSize(2932), cfdp::FileSize(3021));
// Now add 2 segment requests to NAK info and serialize them as well
std::array<NakInfo::SegmentRequest, 2> segReqs = {segReq0, segReq1};
size_t segReqsLen = segReqs.size();
info.setSegmentRequests(segReqs.data(), &segReqsLen, &segReqsLen);
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
result = serializer.serialize(&buffer, &sz, nakBuffer.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
NakPduDeserializer deserializeWithSegReqs(nakBuffer.data(), nakBuffer.size(), info);
result = deserializeWithSegReqs.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
NakInfo::SegmentRequest* segReqsPtr = nullptr;
size_t readSegReqs = 0;
info.getSegmentRequests(&segReqsPtr, &readSegReqs, nullptr);
REQUIRE(readSegReqs == 2);
REQUIRE(segReqsPtr[0].first.getSize() == 2020);
REQUIRE(segReqsPtr[0].second.getSize() == 2520);
REQUIRE(segReqsPtr[1].first.getSize() == 2932);
REQUIRE(segReqsPtr[1].second.getSize() == 3021);
REQUIRE(deserializeWithSegReqs.getPduDataFieldLen() == 25);
REQUIRE(info.getSegmentRequestsLen() == 2);
for (size_t idx = 0; idx < 34; idx++) {
NakPduDeserializer faultyDeserializer(nakBuffer.data(), idx, info);
result = faultyDeserializer.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
for (size_t pduFieldLen = 0; pduFieldLen < 25; pduFieldLen++) {
nakBuffer[1] = (pduFieldLen >> 8) & 0xff;
nakBuffer[2] = pduFieldLen & 0xff;
NakPduDeserializer faultyDeserializer(nakBuffer.data(), nakBuffer.size(), info);
result = faultyDeserializer.parseData();
if (pduFieldLen == 9) {
REQUIRE(info.getSegmentRequestsLen() == 0);
} else if (pduFieldLen == 17) {
REQUIRE(info.getSegmentRequestsLen() == 1);
} else if (pduFieldLen == 25) {
REQUIRE(info.getSegmentRequestsLen() == 2);
}
if (pduFieldLen != 9 and pduFieldLen != 17 and pduFieldLen != 25) {
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
info.setMaxSegmentRequestLen(5);
REQUIRE(info.getSegmentRequestsMaxLen() == 5);
}
}

View File

@ -0,0 +1,65 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/PromptPduDeserializer.h"
#include "fsfw/cfdp/pdu/PromptPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("Prompt PDU", "[PromptPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> rawBuf = {};
uint8_t* buffer = rawBuf.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
SECTION("Serialize") {
PromptPduSerializer serializer(pduConf, cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
result = serializer.serialize(&buffer, &sz, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getWholePduSize() == 12);
REQUIRE(sz == 12);
REQUIRE(serializer.getPduDataFieldLen() == 2);
REQUIRE(rawBuf[10] == FileDirectives::PROMPT);
REQUIRE((rawBuf[sz - 1] >> 7) & 0x01 == cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
for (size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
uint8_t* buffer = rawBuf.data();
size_t sz = 0;
result = serializer.serialize(&buffer, &sz, invalidMaxSz, SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
for (size_t invalidSz = 1; invalidSz < sz; invalidSz++) {
size_t locSz = invalidSz;
uint8_t* buffer = rawBuf.data();
result = serializer.serialize(&buffer, &locSz, sz, SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
SECTION("Deserialize") {
PromptPduSerializer serializer(pduConf, cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
result = serializer.serialize(&buffer, &sz, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
PromptPduDeserializer deserializer(rawBuf.data(), rawBuf.size());
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserializer.getPromptResponseRequired() ==
cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
sz = deserializer.getWholePduSize();
rawBuf[2] = 1;
result = deserializer.parseData();
REQUIRE(result == SerializeIF::STREAM_TOO_SHORT);
rawBuf[2] = 2;
for (size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
deserializer.setData(rawBuf.data(), invalidMaxSz);
result = deserializer.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,330 @@
#include <fsfw/cfdp/tlv/EntityIdTlv.h>
#include <fsfw/cfdp/tlv/FaultHandlerOverrideTlv.h>
#include <fsfw/cfdp/tlv/FilestoreRequestTlv.h>
#include <fsfw/cfdp/tlv/FilestoreResponseTlv.h>
#include <fsfw/cfdp/tlv/FlowLabelTlv.h>
#include <fsfw/cfdp/tlv/MessageToUserTlv.h>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include <string>
#include "fsfw/cfdp/pdu/PduConfig.h"
#include "fsfw/cfdp/tlv/Lv.h"
#include "fsfw/cfdp/tlv/Tlv.h"
#include "fsfw/globalfunctions/arrayprinter.h"
TEST_CASE("CFDP TLV LV", "[CfdpTlvLv]") {
using namespace cfdp;
int result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 255> rawBuf;
uint8_t* serPtr = rawBuf.data();
const uint8_t* deserPtr = rawBuf.data();
size_t deserSize = 0;
cfdp::EntityId sourceId = EntityId(cfdp::WidthInBytes::TWO_BYTES, 0x0ff0);
SECTION("TLV Serialization") {
std::array<uint8_t, 8> tlvRawBuf;
serPtr = tlvRawBuf.data();
result =
sourceId.serialize(&serPtr, &deserSize, tlvRawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 2);
auto tlv = Tlv(TlvTypes::ENTITY_ID, tlvRawBuf.data(), deserSize);
REQUIRE(tlv.getSerializedSize() == 4);
REQUIRE(tlv.getLengthField() == 2);
serPtr = rawBuf.data();
deserSize = 0;
result = tlv.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 4);
REQUIRE(rawBuf[0] == TlvTypes::ENTITY_ID);
REQUIRE(rawBuf[1] == 2);
uint16_t entityId = 0;
SerializeAdapter::deSerialize(&entityId, rawBuf.data() + 2, &deserSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(entityId == 0x0ff0);
// Set new value
sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 12);
serPtr = tlvRawBuf.data();
deserSize = 0;
result =
sourceId.serialize(&serPtr, &deserSize, tlvRawBuf.size(), SerializeIF::Endianness::NETWORK);
tlv.setValue(tlvRawBuf.data(), cfdp::WidthInBytes::FOUR_BYTES);
serPtr = rawBuf.data();
deserSize = 0;
result = tlv.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawBuf[0] == TlvTypes::ENTITY_ID);
REQUIRE(rawBuf[1] == 4);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
serPtr = rawBuf.data();
deserSize = 0;
auto tlvInvalid = Tlv(cfdp::TlvTypes::INVALID_TLV, tlvRawBuf.data(), 0);
REQUIRE(tlvInvalid.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) != HasReturnvaluesIF::RETURN_OK);
tlvInvalid = Tlv(cfdp::TlvTypes::ENTITY_ID, nullptr, 3);
REQUIRE(tlvInvalid.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) != HasReturnvaluesIF::RETURN_OK);
REQUIRE(tlvInvalid.serialize(&serPtr, &deserSize, 0, SerializeIF::Endianness::NETWORK) !=
HasReturnvaluesIF::RETURN_OK);
REQUIRE(tlvInvalid.getSerializedSize() == 0);
REQUIRE(tlvInvalid.serialize(nullptr, nullptr, 0, SerializeIF::Endianness::NETWORK) !=
HasReturnvaluesIF::RETURN_OK);
Tlv zeroLenField(TlvTypes::FAULT_HANDLER, nullptr, 0);
REQUIRE(zeroLenField.getSerializedSize() == 2);
serPtr = rawBuf.data();
deserSize = 0;
REQUIRE(zeroLenField.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) ==
HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawBuf[0] == TlvTypes::FAULT_HANDLER);
REQUIRE(rawBuf[1] == 0);
}
SECTION("TLV Deserialization") {
// Serialization was tested before, generate raw data now
std::array<uint8_t, 8> tlvRawBuf;
serPtr = tlvRawBuf.data();
result =
sourceId.serialize(&serPtr, &deserSize, tlvRawBuf.size(), SerializeIF::Endianness::NETWORK);
auto tlvSerialization = Tlv(TlvTypes::ENTITY_ID, tlvRawBuf.data(), deserSize);
serPtr = rawBuf.data();
deserSize = 0;
result = tlvSerialization.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
Tlv tlv;
deserPtr = rawBuf.data();
result = tlv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(tlv.getSerializedSize() == 4);
REQUIRE(tlv.getType() == TlvTypes::ENTITY_ID);
deserPtr = tlv.getValue();
uint16_t entityId = 0;
deserSize = 0;
SerializeAdapter::deSerialize(&entityId, deserPtr, &deserSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(entityId == 0x0ff0);
REQUIRE(tlv.deSerialize(nullptr, nullptr, SerializeIF::Endianness::NETWORK) !=
HasReturnvaluesIF::RETURN_OK);
deserPtr = rawBuf.data();
deserSize = 0;
REQUIRE(tlv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK) ==
SerializeIF::STREAM_TOO_SHORT);
// Set invalid TLV
rawBuf[0] = TlvTypes::INVALID_TLV;
deserSize = 4;
REQUIRE(tlv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK) !=
HasReturnvaluesIF::RETURN_OK);
Tlv zeroLenField(TlvTypes::FAULT_HANDLER, nullptr, 0);
serPtr = rawBuf.data();
deserSize = 0;
REQUIRE(zeroLenField.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) ==
HasReturnvaluesIF::RETURN_OK);
deserPtr = rawBuf.data();
result = zeroLenField.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(zeroLenField.getSerializedSize() == 2);
REQUIRE(deserSize == 0);
}
SECTION("LV Serialization") {
std::array<uint8_t, 8> lvRawBuf;
serPtr = lvRawBuf.data();
result =
sourceId.serialize(&serPtr, &deserSize, lvRawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 2);
auto lv = cfdp::Lv(lvRawBuf.data(), 2);
auto lvCopy = cfdp::Lv(lv);
REQUIRE(lv.getSerializedSize() == 3);
REQUIRE(lvCopy.getSerializedSize() == 3);
REQUIRE(lv.getValue(nullptr) == lvCopy.getValue(nullptr));
serPtr = rawBuf.data();
deserSize = 0;
result = lv.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 3);
REQUIRE(rawBuf[0] == 2);
uint16_t sourceId = 0;
result = SerializeAdapter::deSerialize(&sourceId, rawBuf.data() + 1, &deserSize,
SerializeIF::Endianness::BIG);
REQUIRE(sourceId == 0x0ff0);
auto lvEmpty = Lv(nullptr, 0);
REQUIRE(lvEmpty.getSerializedSize() == 1);
serPtr = rawBuf.data();
deserSize = 0;
result =
lvEmpty.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 1);
}
SECTION("LV Deserialization") {
std::array<uint8_t, 8> lvRawBuf;
serPtr = lvRawBuf.data();
result =
sourceId.serialize(&serPtr, &deserSize, lvRawBuf.size(), SerializeIF::Endianness::NETWORK);
auto lv = cfdp::Lv(lvRawBuf.data(), 2);
serPtr = rawBuf.data();
deserSize = 0;
result = lv.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
Lv uninitLv;
deserPtr = rawBuf.data();
deserSize = 3;
result = uninitLv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::BIG);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(uninitLv.getSerializedSize() == 3);
const uint8_t* storedValue = uninitLv.getValue(nullptr);
uint16_t sourceId = 0;
result = SerializeAdapter::deSerialize(&sourceId, storedValue, &deserSize,
SerializeIF::Endianness::BIG);
REQUIRE(sourceId == 0x0ff0);
auto lvEmpty = Lv(nullptr, 0);
REQUIRE(lvEmpty.getSerializedSize() == 1);
serPtr = rawBuf.data();
deserSize = 0;
result =
lvEmpty.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 1);
deserPtr = rawBuf.data();
result = uninitLv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::BIG);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(uninitLv.getSerializedSize() == 1);
REQUIRE(uninitLv.deSerialize(nullptr, nullptr, SerializeIF::Endianness::BIG) ==
HasReturnvaluesIF::RETURN_FAILED);
serPtr = rawBuf.data();
deserSize = 0;
REQUIRE(uninitLv.serialize(&serPtr, &deserSize, 0, SerializeIF::Endianness::BIG) ==
SerializeIF::BUFFER_TOO_SHORT);
REQUIRE(uninitLv.serialize(nullptr, nullptr, 12, SerializeIF::Endianness::BIG));
deserSize = 0;
REQUIRE(uninitLv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::BIG) ==
SerializeIF::STREAM_TOO_SHORT);
}
SECTION("Filestore Response TLV") {
std::string name = "hello.txt";
cfdp::Lv firstName(reinterpret_cast<const uint8_t*>(name.data()), name.size());
std::string name2 = "hello2.txt";
cfdp::Lv secondName(reinterpret_cast<const uint8_t*>(name2.data()), name2.size());
std::string msg = "12345";
cfdp::Lv fsMsg(reinterpret_cast<const uint8_t*>(msg.data()), msg.size());
FilestoreResponseTlv response(cfdp::FilestoreActionCode::APPEND_FILE, cfdp::FSR_SUCCESS,
firstName, &fsMsg);
response.setSecondFileName(&secondName);
REQUIRE(response.getLengthField() == 10 + 11 + 6 + 1);
REQUIRE(response.getSerializedSize() == response.getLengthField() + 2);
cfdp::Tlv rawResponse;
std::array<uint8_t, 128> serBuf = {};
result = response.convertToTlv(rawResponse, serBuf.data(), serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawResponse.getType() == cfdp::TlvTypes::FILESTORE_RESPONSE);
cfdp::Lv emptyMsg;
cfdp::Lv emptySecondName;
FilestoreResponseTlv emptyTlv(firstName, &emptyMsg);
emptyTlv.setSecondFileName(&emptySecondName);
result = emptyTlv.deSerialize(rawResponse, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyTlv.getActionCode() == cfdp::FilestoreActionCode::APPEND_FILE);
REQUIRE(emptyTlv.getStatusCode() == cfdp::FSR_SUCCESS);
size_t firstNameLen = 0;
const char* firstNamePtr =
reinterpret_cast<const char*>(emptyTlv.getFirstFileName().getValue(&firstNameLen));
auto helloString = std::string(firstNamePtr, firstNameLen);
REQUIRE(helloString == "hello.txt");
}
SECTION("Filestore Request TLV") {
std::string name = "hello.txt";
cfdp::Lv firstName(reinterpret_cast<const uint8_t*>(name.data()), name.size());
std::string name2 = "hello2.txt";
cfdp::Lv secondName(reinterpret_cast<const uint8_t*>(name2.data()), name2.size());
FilestoreRequestTlv request(cfdp::FilestoreActionCode::APPEND_FILE, firstName);
// second name not set yet
REQUIRE(request.getLengthField() == 10 + 1);
REQUIRE(request.getSerializedSize() == request.getLengthField() + 2);
std::array<uint8_t, 128> serBuf = {};
uint8_t* ptr = serBuf.data();
size_t sz = 0;
result = request.serialize(&ptr, &sz, serBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == cfdp::FILESTORE_REQUIRES_SECOND_FILE);
ptr = serBuf.data();
sz = 0;
request.setSecondFileName(&secondName);
size_t expectedSz = request.getLengthField();
REQUIRE(expectedSz == 10 + 11 + 1);
REQUIRE(request.getSerializedSize() == expectedSz + 2);
result = request.serialize(&ptr, &sz, serBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(sz == expectedSz + 2);
FilestoreRequestTlv emptyRequest(firstName);
emptyRequest.setSecondFileName(&secondName);
const uint8_t* constptr = serBuf.data();
result = emptyRequest.deSerialize(&constptr, &sz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
cfdp::Tlv rawRequest;
ptr = serBuf.data();
sz = 0;
result = request.convertToTlv(rawRequest, serBuf.data(), serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawRequest.getType() == cfdp::TlvTypes::FILESTORE_REQUEST);
emptyRequest.setActionCode(cfdp::FilestoreActionCode::DELETE_FILE);
result = emptyRequest.deSerialize(rawRequest, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyRequest.getType() == cfdp::TlvTypes::FILESTORE_REQUEST);
REQUIRE(emptyRequest.getActionCode() == cfdp::FilestoreActionCode::APPEND_FILE);
}
SECTION("Other") {
MessageToUserTlv emptyTlv;
uint8_t flowLabel = 1;
FlowLabelTlv flowLabelTlv(&flowLabel, 1);
FaultHandlerOverrideTlv faultOverrideTlv(cfdp::ConditionCode::FILESTORE_REJECTION,
cfdp::FaultHandlerCode::NOTICE_OF_CANCELLATION);
size_t sz = 0;
ReturnValue_t result =
faultOverrideTlv.serialize(&serPtr, &sz, rawBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(faultOverrideTlv.getSerializedSize() == 3);
REQUIRE(sz == 3);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FaultHandlerOverrideTlv emptyOverrideTlv;
result = emptyOverrideTlv.deSerialize(&deserPtr, &sz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
EntityId entId(cfdp::WidthInBytes::TWO_BYTES, 0x42);
EntityId emptyId;
EntityIdTlv idTlv(emptyId);
serPtr = rawBuf.data();
result = idTlv.serialize(&serPtr, &deserSize, rawBuf.size(), SerializeIF::Endianness::NETWORK);
cfdp::Tlv rawTlv(cfdp::TlvTypes::ENTITY_ID, rawBuf.data() + 2, 2);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
deserPtr = rawBuf.data();
result = idTlv.deSerialize(rawTlv, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
}
}

View File

@ -0,0 +1,10 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
RingBufferTest.cpp
TestArrayList.cpp
TestDynamicFifo.cpp
TestFifo.cpp
TestFixedArrayList.cpp
TestFixedMap.cpp
TestFixedOrderedMultimap.cpp
TestPlacementFactory.cpp
)

View File

@ -0,0 +1,321 @@
#include <fsfw/container/SimpleRingBuffer.h>
#include <catch2/catch_test_macros.hpp>
#include <cstring>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("Ring Buffer Test", "[RingBufferTest]") {
uint8_t testData[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
SimpleRingBuffer ringBuffer(10, false, 5);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.writeTillWrap() == 2);
// too many excess bytes.
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 3);
std::memcpy(testPtr, testData, 5);
ringBuffer.confirmBytesWritten(5);
REQUIRE(ringBuffer.getAvailableReadData() == 5);
ringBuffer.readData(readBuffer, 5, true);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for (uint8_t i = 0; i < 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
}
TEST_CASE("Ring Buffer Test2", "[RingBufferTest2]") {
uint8_t testData[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
uint8_t *newBuffer = new uint8_t[15];
SimpleRingBuffer ringBuffer(newBuffer, 10, true, 5);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.writeTillWrap() == 2);
// too many excess bytes.
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 3);
std::memcpy(testPtr, testData, 5);
ringBuffer.confirmBytesWritten(5);
REQUIRE(ringBuffer.getAvailableReadData() == 5);
ringBuffer.readData(readBuffer, 5, true);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for (uint8_t i = 0; i < 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
SECTION("Overflow") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
// Writing more than the buffer is large, technically thats allowed
// But it is senseless and has undesired impact on read call
REQUIRE(ringBuffer.writeData(testData, 13) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
ringBuffer.clear();
uint8_t *ptr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&ptr, 13) == retval::CATCH_OK);
REQUIRE(ptr != nullptr);
memcpy(ptr, testData, 13);
ringBuffer.confirmBytesWritten(13);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 3, true) == retval::CATCH_OK);
for (auto i = 0; i < 3; i++) {
REQUIRE(readBuffer[i] == testData[i + 10]);
}
}
}
TEST_CASE("Ring Buffer Test3", "[RingBufferTest3]") {
uint8_t testData[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
uint8_t *newBuffer = new uint8_t[25];
SimpleRingBuffer ringBuffer(newBuffer, 10, true, 15);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 8);
REQUIRE(ringBuffer.writeTillWrap() == 2);
// too many excess bytes.
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED);
// Less Execss bytes overwrites before
REQUIRE(ringBuffer.getFreeElement(&testPtr, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 1);
std::memcpy(testPtr, testData, 3);
ringBuffer.confirmBytesWritten(3);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
ringBuffer.readData(readBuffer, 3, true);
for (uint8_t i = 0; i < 3; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for (uint8_t i = 0; i < 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
SECTION("Overflow") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
// Writing more than the buffer is large, technically thats allowed
// But it is senseless and has undesired impact on read call
REQUIRE(ringBuffer.writeData(testData, 13) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
ringBuffer.clear();
uint8_t *ptr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&ptr, 13) == retval::CATCH_OK);
REQUIRE(ptr != nullptr);
memcpy(ptr, testData, 13);
ringBuffer.confirmBytesWritten(13);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 3, true) == retval::CATCH_OK);
for (auto i = 0; i < 3; i++) {
REQUIRE(readBuffer[i] == testData[i + 10]);
}
}
}
TEST_CASE("Ring Buffer Test4", "[RingBufferTest4]") {
uint8_t testData[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
SimpleRingBuffer ringBuffer(10, false, 15);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for (uint8_t i = 0; i < 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.writeTillWrap() == 2);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 3);
std::memcpy(testPtr, testData, 5);
ringBuffer.confirmBytesWritten(5);
REQUIRE(ringBuffer.getAvailableReadData() == 5);
ringBuffer.readData(readBuffer, 5, true);
for (uint8_t i = 0; i < 5; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for (uint8_t i = 0; i < 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
}

View File

@ -0,0 +1,92 @@
#include <fsfw/container/ArrayList.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
/**
* @brief Array List test
*/
TEST_CASE("Array List", "[ArrayListTest]") {
// perform set-up here
ArrayList<uint16_t> list(20);
struct TestClass {
public:
TestClass(){};
TestClass(uint32_t number1, uint64_t number2) : number1(number1), number2(number2){};
uint32_t number1 = -1;
uint64_t number2 = -1;
bool operator==(const TestClass& other) {
return ((this->number1 == other.number1) and (this->number2 == other.number2));
};
};
ArrayList<TestClass> complexList(20);
SECTION("SimpleTest") {
REQUIRE(list.maxSize() == 20);
REQUIRE(list.size == 0);
REQUIRE(list.insert(10) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(list[0] == 10);
REQUIRE(list.front() != nullptr);
REQUIRE((*list.front()) == 10);
REQUIRE(list.back() != nullptr);
REQUIRE((*list.back()) == 10);
// Need to test the const version of back as well
const uint16_t* number = const_cast<const ArrayList<uint16_t>*>(&list)->back();
REQUIRE(*number == 10);
list.clear();
REQUIRE(list.size == 0);
}
SECTION("Fill and check") {
// This is an invalid element but its not a nullptr
REQUIRE(list.back() != nullptr);
for (auto i = 0; i < 20; i++) {
REQUIRE(list.insert(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(list.insert(20) == static_cast<int>(ArrayList<uint16_t>::FULL));
ArrayList<uint16_t>::Iterator it = list.begin();
REQUIRE((*it) == 0);
it++;
REQUIRE((*it) == 1);
it--;
REQUIRE((*it) == 0);
it++;
for (auto it2 = list.begin(); it2 != list.end(); it2++) {
if (it == it2) {
REQUIRE((*it) == (*it2));
break;
} else {
REQUIRE((*it2) == 0);
REQUIRE(it2 != it);
}
}
}
SECTION("Const Iterator") {
ArrayList<uint16_t>::Iterator it = list.begin();
for (auto i = 0; i < 10; i++) {
REQUIRE(list.insert(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
it++;
const uint16_t* number = it.value;
REQUIRE(*number == 1);
}
SECTION("Const Iterator") {
ArrayList<TestClass>::Iterator it = complexList.begin();
for (auto i = 0; i < 10; i++) {
REQUIRE(complexList.insert(TestClass(i, i + 1)) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
it++;
const TestClass* secondTest = it.value;
bool compare = TestClass(1, 2) == *secondTest;
REQUIRE(compare);
it++;
REQUIRE(it->number1 == 2);
REQUIRE(it->number2 == 3);
const ArrayList<TestClass>::Iterator it4(&(complexList[2]));
REQUIRE(it4->number1 == 2);
REQUIRE((*it4).number2 == 3);
REQUIRE(complexList.remaining() == 10);
}
}

View File

@ -0,0 +1,145 @@
#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/FIFO.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("Dynamic Fifo Tests", "[TestDynamicFifo]") {
INFO("Dynamic Fifo Tests");
struct Test {
uint64_t number1;
uint32_t number2;
uint8_t number3;
bool operator==(struct Test& other) {
if ((other.number1 == this->number1) and (other.number1 == this->number1) and
(other.number1 == this->number1)) {
return true;
}
return false;
}
};
DynamicFIFO<Test> fifo(3);
std::vector<Test> list;
struct Test structOne({UINT64_MAX, UINT32_MAX, UINT8_MAX});
struct Test structTwo({0, 1, 2});
struct Test structThree({42, 43, 44});
list.push_back(structThree);
list.push_back(structTwo);
list.push_back(structOne);
SECTION("Insert, retrieval test") {
REQUIRE(fifo.getMaxCapacity() == 3);
REQUIRE(fifo.size() == 0);
REQUIRE(fifo.empty());
REQUIRE(not fifo.full());
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(FIFOBase<Test>::FULL));
struct Test testptr;
REQUIRE(fifo.peek(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == structOne;
REQUIRE(equal);
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
for (size_t i = 2; i < 3; i--) {
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo.size() == i);
}
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr) == static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.peek(&testptr) == static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(not fifo.full());
REQUIRE(fifo.empty());
REQUIRE(fifo.pop() == static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 1);
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 2);
REQUIRE(fifo.pop() == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 1);
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.peek(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == structTwo;
REQUIRE(equal);
REQUIRE(fifo.pop() == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 0);
REQUIRE(fifo.empty());
// struct Test* ptr = nullptr;
// REQUIRE(fifo.retrieve(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
// REQUIRE(fifo.peek(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
};
SECTION("Copy Test") {
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
DynamicFIFO<Test> fifo2(fifo);
REQUIRE(fifo2.size() == 3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
};
SECTION("Assignment Test") {
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
DynamicFIFO<Test> fifo2(6);
fifo2 = fifo;
REQUIRE(fifo2.size() == 3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for (size_t i = 2; i < 3; i--) {
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size() == i);
}
};
SECTION("Assignment Test Smaller") {
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
DynamicFIFO<Test> fifo2(2);
fifo2 = fifo;
REQUIRE(fifo2.size() == 3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for (size_t i = 2; i < 3; i--) {
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size() == i);
}
};
};

View File

@ -0,0 +1,133 @@
#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/FIFO.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("Static Fifo Tests", "[TestFifo]") {
INFO("Fifo Tests");
struct Test {
uint64_t number1;
uint32_t number2;
uint8_t number3;
bool operator==(struct Test& other) {
if ((other.number1 == this->number1) and (other.number1 == this->number1) and
(other.number1 == this->number1)) {
return true;
}
return false;
}
};
FIFO<Test, 3> fifo;
std::vector<Test> list;
struct Test structOne({UINT64_MAX, UINT32_MAX, UINT8_MAX});
struct Test structTwo({0, 1, 2});
struct Test structThree({42, 43, 44});
list.push_back(structThree);
list.push_back(structTwo);
list.push_back(structOne);
SECTION("Insert, retrieval test") {
REQUIRE(fifo.getMaxCapacity() == 3);
REQUIRE(fifo.size() == 0);
REQUIRE(fifo.empty());
REQUIRE(not fifo.full());
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(FIFOBase<Test>::FULL));
struct Test testptr;
REQUIRE(fifo.peek(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == structOne;
REQUIRE(equal);
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
for (size_t i = 2; i < 3; i--) {
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo.size() == i);
}
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr) == static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.peek(&testptr) == static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(not fifo.full());
REQUIRE(fifo.empty());
REQUIRE(fifo.pop() == static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 1);
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 2);
REQUIRE(fifo.pop() == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 1);
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
// Test that retrieve and peek will not cause a nullptr dereference
struct Test* ptr = nullptr;
REQUIRE(fifo.retrieve(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
REQUIRE(fifo.peek(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
REQUIRE(fifo.peek(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == structTwo;
REQUIRE(equal);
REQUIRE(fifo.pop() == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 0);
REQUIRE(fifo.empty());
};
SECTION("Copy Test") {
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
FIFO<Test, 3> fifo2(fifo);
REQUIRE(fifo2.size() == 3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for (size_t i = 2; i < 3; i--) {
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size() == i);
}
};
SECTION("Assignment Test") {
REQUIRE(fifo.insert(structOne) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size() == 3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
FIFO<Test, 3> fifo2;
fifo2 = fifo;
REQUIRE(fifo2.size() == 3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for (size_t i = 2; i < 3; i--) {
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size() == i);
}
};
};

View File

@ -0,0 +1,38 @@
#include <fsfw/container/FixedArrayList.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("FixedArrayList Tests", "[TestFixedArrayList]") {
INFO("FixedArrayList Tests");
using testList = FixedArrayList<uint32_t, 260, uint16_t>;
testList list;
REQUIRE(list.size == 0);
REQUIRE(list.insert(10) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(list.size == 1);
REQUIRE(list.maxSize() == 260);
SECTION("Copy Constructor") {
testList list2(list);
REQUIRE(list2.size == 1);
REQUIRE(list2[0] == 10);
REQUIRE(list.maxSize() == 260);
};
SECTION("Assignment copy") {
testList list2;
REQUIRE(list2.size == 0);
list2 = list;
REQUIRE(list2.size == 1);
REQUIRE(list2[0] == 10);
REQUIRE(list.maxSize() == 260);
};
SECTION("Fill") {
for (auto i = 1; i < 260; i++) {
REQUIRE(list.insert(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(list.insert(260) == static_cast<int>(ArrayList<uint32_t, uint16_t>::FULL));
list.clear();
REQUIRE(list.size == 0);
}
}

View File

@ -0,0 +1,166 @@
#include <fsfw/container/FixedMap.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
template class FixedMap<unsigned int, unsigned short>;
TEST_CASE("FixedMap Tests", "[TestFixedMap]") {
INFO("FixedMap Tests");
FixedMap<unsigned int, unsigned short> map(30);
REQUIRE(map.size() == 0);
REQUIRE(map.maxSize() == 30);
REQUIRE(map.getSerializedSize() == sizeof(uint32_t));
REQUIRE(map.empty());
REQUIRE(not map.full());
SECTION("Fill and erase") {
for (uint16_t i = 0; i < 30; i++) {
REQUIRE(map.insert(std::make_pair(i, i + 1)) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.exists(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(i)->second == i + 1);
REQUIRE(not map.empty());
}
REQUIRE(map.insert(0, 0) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_ALREADY_EXISTS));
REQUIRE(map.insert(31, 0) == static_cast<int>(FixedMap<uint32_t, uint16_t>::MAP_FULL));
REQUIRE(map.exists(31) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 30);
REQUIRE(map.full());
{
uint16_t* ptr;
REQUIRE(map.find(5, &ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(*ptr == 6);
REQUIRE(*(map.findValue(6)) == 7);
REQUIRE(map.find(31, &ptr) ==
static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
}
REQUIRE(map.getSerializedSize() ==
(sizeof(uint32_t) + 30 * (sizeof(uint32_t) + sizeof(uint16_t))));
REQUIRE(map.erase(2) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.erase(31) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.exists(2) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 29);
for (auto element : map) {
if (element.first == 5) {
REQUIRE(element.second == 6);
}
}
for (FixedMap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++) {
REQUIRE(it->second == it->first + 1);
REQUIRE((*it).second == (*it).first + 1);
it->second = it->second + 1;
REQUIRE(it->second == it->first + 2);
}
for (FixedMap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++) {
REQUIRE(map.erase(&it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(map.size() == 0);
for (FixedMap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++) {
// This line should never executed if begin and end is correct
FAIL("Should never be reached, Iterators invalid");
}
};
SECTION("Insert variants") {
FixedMap<uint32_t, uint16_t>::Iterator it = map.end();
REQUIRE(map.insert(36, 37, &it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(it->first == 36);
REQUIRE(it->second == 37);
REQUIRE(map.size() == 1);
REQUIRE(map.insert(37, 38, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(37)->second == 38);
REQUIRE(map.size() == 2);
REQUIRE(map.insert(37, 24, nullptr) ==
static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_ALREADY_EXISTS));
REQUIRE(map.find(37)->second != 24);
REQUIRE(map.size() == 2);
};
SECTION("Serialize and DeSerialize") {
REQUIRE(map.insert(36, 37, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.insert(37, 38, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
uint8_t buffer[sizeof(uint32_t) + 2 * (sizeof(uint32_t) + sizeof(uint16_t))];
REQUIRE(map.getSerializedSize() ==
(sizeof(uint32_t) + 2 * (sizeof(uint32_t) + sizeof(uint16_t))));
uint8_t* loc_ptr = buffer;
size_t size = 0;
REQUIRE(map.serialize(&loc_ptr, &size, 10, SerializeIF::Endianness::BIG) ==
static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
loc_ptr = buffer;
size = 0;
REQUIRE(map.serialize(
&loc_ptr, &size, sizeof(uint32_t) + 2 * (sizeof(uint32_t) + sizeof(uint16_t)),
SerializeIF::Endianness::BIG) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(size == 16);
uint32_t internal_size = 0;
const uint8_t* ptr2 = buffer;
REQUIRE(
SerializeAdapter::deSerialize(&internal_size, &ptr2, &size, SerializeIF::Endianness::BIG) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(internal_size == 2);
for (uint8_t i = 36; i < 38; i++) {
uint32_t first_element = 0;
REQUIRE(SerializeAdapter::deSerialize(&first_element, &ptr2, &size,
SerializeIF::Endianness::BIG) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(first_element == i);
uint16_t second_element = 0;
REQUIRE(SerializeAdapter::deSerialize(&second_element, &ptr2, &size,
SerializeIF::Endianness::BIG) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(second_element == i + 1);
}
REQUIRE(size == 0);
map.clear();
const uint8_t* constPtr = buffer;
size = 16;
REQUIRE(map.size() == 0);
REQUIRE(map.deSerialize(&constPtr, &size, SerializeIF::Endianness::BIG) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.size() == 2);
REQUIRE(map.find(36)->second == 37);
for (auto& element : map) {
REQUIRE((element.first + 1) == element.second);
}
};
SECTION("Failed erase and deSerialize") {
FixedMap<uint32_t, uint16_t>::Iterator it;
std::pair<uint32_t, uint16_t> pair = std::make_pair(44, 43);
it = FixedMap<uint32_t, uint16_t>::Iterator(&pair);
REQUIRE(map.erase(&it) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.find(45) == map.end());
size_t toLargeMap = 100;
const uint8_t* ptr = reinterpret_cast<uint8_t*>(&toLargeMap);
size_t size = sizeof(size_t);
REQUIRE(map.deSerialize(&ptr, &size, SerializeIF::Endianness::BIG) ==
static_cast<int>(SerializeIF::TOO_MANY_ELEMENTS));
};
SECTION("Little Endianess") {
map.clear();
map.insert(10, 20, nullptr);
uint8_t newBuffer[sizeof(uint32_t) + 1 * (sizeof(uint32_t) + sizeof(uint16_t))];
uint8_t* ptr = newBuffer;
size_t size = 0;
size_t max_size = sizeof(uint32_t) + 1 * (sizeof(uint32_t) + sizeof(uint16_t));
REQUIRE(map.serialize(&ptr, &size, max_size, SerializeIF::Endianness::LITTLE) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
map.clear();
REQUIRE(map.size() == 0);
const uint8_t* ptr2 = newBuffer;
REQUIRE(map.deSerialize(&ptr2, &size, SerializeIF::Endianness::LITTLE) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.size() == 1);
REQUIRE(map.find(10)->second == 20);
};
}

View File

@ -0,0 +1,221 @@
#include <fsfw/container/FixedOrderedMultimap.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("FixedOrderedMultimap Tests", "[TestFixedOrderedMultimap]") {
INFO("FixedOrderedMultimap Tests");
FixedOrderedMultimap<unsigned int, unsigned short> map(30);
REQUIRE(map.size() == 0);
REQUIRE(map.maxSize() == 30);
SECTION("Test insert, find, exists") {
for (uint16_t i = 0; i < 30; i++) {
REQUIRE(map.insert(std::make_pair(i, i + 1)) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.exists(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(i)->second == i + 1);
}
REQUIRE(map.insert(0, 0) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::MAP_FULL));
REQUIRE(map.exists(31) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 30);
{
uint16_t* ptr;
REQUIRE(map.find(5, &ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(*ptr == 6);
REQUIRE(map.find(31, &ptr) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
}
REQUIRE(map.erase(2) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.erase(31) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.exists(2) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 29);
for (auto element : map) {
if (element.first == 5) {
REQUIRE(element.second == 6);
}
}
for (FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end();
it++) {
REQUIRE(it->second == it->first + 1);
REQUIRE((*it).second == (*it).first + 1);
it->second = it->second + 1;
REQUIRE(it->second == it->first + 2);
}
{
FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.begin();
while (it != map.end()) {
REQUIRE(map.erase(&it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(map.size() == 0);
}
for (FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end();
it++) {
// This line should never executed if begin and end is correct
FAIL("Should never be reached, Iterators invalid");
}
};
SECTION("Test different insert variants") {
FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.end();
REQUIRE(map.insert(36, 37, &it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(it->first == 36);
REQUIRE(it->second == 37);
REQUIRE(map.size() == 1);
REQUIRE(map.insert(37, 38, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(37)->second == 38);
REQUIRE(map.size() == 2);
REQUIRE(map.insert(37, 24, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(37)->second == 38);
REQUIRE(map.insert(0, 1, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(0)->second == 1);
REQUIRE(map.size() == 4);
map.clear();
REQUIRE(map.size() == 0);
}
SECTION("Test different erase and find with no entries") {
FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it;
it = map.end();
REQUIRE(map.erase(&it) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.find(1) == map.end());
}
}
TEST_CASE("FixedOrderedMultimap Non Trivial Type", "[TestFixedOrderedMultimapNonTrivial]") {
INFO("FixedOrderedMultimap Non Trivial Type");
class TestClass {
public:
TestClass(){};
TestClass(uint32_t number1, uint64_t number2) : number1(number1), number2(number2){};
~TestClass(){};
bool operator==(const TestClass& lhs) {
return ((this->number1 == lhs.number1) and (this->number2 == lhs.number2));
}
bool operator!=(const TestClass& lhs) { return not(this->operator==(lhs)); }
TestClass(const TestClass& other) {
this->number1 = other.number1;
this->number2 = other.number2;
};
TestClass& operator=(const TestClass& other) {
this->number1 = other.number1;
this->number2 = other.number2;
return *this;
};
private:
uint32_t number1 = 0;
uint64_t number2 = 5;
};
FixedOrderedMultimap<unsigned int, TestClass> map(30);
REQUIRE(map.size() == 0);
REQUIRE(map.maxSize() == 30);
SECTION("Test insert, find, exists") {
for (uint16_t i = 0; i < 30; i++) {
REQUIRE(map.insert(std::make_pair(i, TestClass(i + 1, i))) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.exists(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool compare = map.find(i)->second == TestClass(i + 1, i);
REQUIRE(compare);
}
REQUIRE(map.insert(0, TestClass()) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::MAP_FULL));
REQUIRE(map.exists(31) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 30);
{
TestClass* ptr = nullptr;
REQUIRE(map.find(5, &ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool compare = *ptr == TestClass(6, 5);
REQUIRE(compare);
REQUIRE(map.find(31, &ptr) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
}
REQUIRE(map.erase(2) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.erase(31) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.exists(2) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 29);
for (auto element : map) {
if (element.first == 5) {
bool compare = element.second == TestClass(6, 5);
REQUIRE(compare);
}
}
for (FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.begin(); it != map.end();
it++) {
bool compare = it->second == TestClass(it->first + 1, it->first);
REQUIRE(compare);
compare = (*it).second == TestClass((*it).first + 1, (*it).first);
REQUIRE(compare);
it->second = TestClass(it->first + 2, it->first);
compare = it->second == TestClass(it->first + 2, it->first);
REQUIRE(compare);
}
{
FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.begin();
while (it != map.end()) {
REQUIRE(map.erase(&it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(map.size() == 0);
}
for (FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.begin(); it != map.end();
it++) {
// This line should never executed if begin and end is correct
FAIL("Should never be reached, Iterators invalid");
}
};
SECTION("Test different insert variants") {
FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.end();
REQUIRE(map.insert(36, TestClass(37, 36), &it) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(it->first == 36);
bool compare = it->second == TestClass(37, 36);
REQUIRE(compare);
REQUIRE(map.size() == 1);
REQUIRE(map.insert(37, TestClass(38, 37), nullptr) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
compare = map.find(37)->second == TestClass(38, 37);
REQUIRE(compare);
REQUIRE(map.size() == 2);
REQUIRE(map.insert(37, TestClass(24, 37), nullptr) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
compare = map.find(37)->second == TestClass(38, 37);
REQUIRE(compare);
REQUIRE(map.insert(0, TestClass(1, 0), nullptr) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
compare = map.find(0)->second == TestClass(1, 0);
REQUIRE(compare);
REQUIRE(map.size() == 4);
map.clear();
REQUIRE(map.size() == 0);
}
SECTION("Test different erase and find with no entries") {
FixedOrderedMultimap<uint32_t, TestClass>::Iterator it;
it = map.end();
REQUIRE(map.erase(&it) ==
static_cast<int>(FixedOrderedMultimap<uint32_t, TestClass>::KEY_DOES_NOT_EXIST));
REQUIRE(map.find(1) == map.end());
}
}

View File

@ -0,0 +1,48 @@
#include <fsfw/container/ArrayList.h>
#include <fsfw/container/PlacementFactory.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <fsfw/storagemanager/LocalPool.h>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("PlacementFactory Tests", "[TestPlacementFactory]") {
INFO("PlacementFactory Tests");
LocalPool::LocalPoolConfig poolCfg = {
{1, sizeof(uint16_t)}, {1, sizeof(uint32_t)}, {1, sizeof(uint64_t)}};
// const uint16_t element_sizes[3] = {sizeof(uint16_t), sizeof(uint32_t), sizeof(uint64_t)};
// const uint16_t n_elements[3] = {1, 1, 1};
LocalPool storagePool(0x1, poolCfg, false, true);
PlacementFactory factory(&storagePool);
SECTION("Pool overload") {
store_address_t address;
uint8_t* ptr = nullptr;
REQUIRE(storagePool.getFreeElement(&address, sizeof(ArrayList<uint32_t, uint16_t>), &ptr) ==
static_cast<int>(StorageManagerIF::DATA_TOO_LARGE));
ArrayList<uint32_t, uint16_t>* list2 = factory.generate<ArrayList<uint32_t, uint16_t> >(80);
REQUIRE(list2 == nullptr);
}
SECTION("Test generate and destroy") {
uint64_t* number = factory.generate<uint64_t>(32000);
REQUIRE(number != nullptr);
REQUIRE(*number == 32000);
store_address_t address;
uint8_t* ptr = nullptr;
REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr) ==
static_cast<int>(StorageManagerIF::DATA_TOO_LARGE));
uint64_t* number2 = factory.generate<uint64_t>(12345);
REQUIRE(number2 == nullptr);
REQUIRE(factory.destroy(number) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr) ==
static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(storagePool.deleteData(address) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
// Check that PlacementFactory checks for nullptr
ptr = nullptr;
REQUIRE(factory.destroy(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
}
}

View File

@ -0,0 +1,7 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
LocalPoolVariableTest.cpp
LocalPoolVectorTest.cpp
DataSetTest.cpp
LocalPoolManagerTest.cpp
LocalPoolOwnerBase.cpp
)

View File

@ -0,0 +1,286 @@
#include <fsfw/datapool/PoolReadGuard.h>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/SharedLocalDataSet.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/globalfunctions/bitutility.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "LocalPoolOwnerBase.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
#include "tests/TestsConfig.h"
TEST_CASE("DataSetTest", "[DataSetTest]") {
LocalPoolOwnerBase* poolOwner =
ObjectManager::instance()->get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
REQUIRE(poolOwner != nullptr);
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK);
LocalPoolStaticTestDataSet localSet;
SECTION("BasicTest") {
/* Test some basic functions */
CHECK(localSet.getReportingEnabled() == false);
CHECK(localSet.getLocalPoolIdsSerializedSize(false) == 3 * sizeof(lp_id_t));
CHECK(localSet.getLocalPoolIdsSerializedSize(true) == 3 * sizeof(lp_id_t) + sizeof(uint8_t));
CHECK(localSet.getSid() == lpool::testSid);
CHECK(localSet.getCreatorObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE);
size_t maxSize = localSet.getLocalPoolIdsSerializedSize(true);
uint8_t localPoolIdBuff[maxSize];
/* Skip size field */
lp_id_t* lpIds = reinterpret_cast<lp_id_t*>(localPoolIdBuff + 1);
size_t serSize = 0;
uint8_t* localPoolIdBuffPtr = reinterpret_cast<uint8_t*>(localPoolIdBuff);
/* Test local pool ID serialization */
CHECK(localSet.serializeLocalPoolIds(&localPoolIdBuffPtr, &serSize, maxSize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
CHECK(serSize == maxSize);
CHECK(localPoolIdBuff[0] == 3);
CHECK(lpIds[0] == localSet.localPoolVarUint8.getDataPoolId());
CHECK(lpIds[1] == localSet.localPoolVarFloat.getDataPoolId());
CHECK(lpIds[2] == localSet.localPoolUint16Vec.getDataPoolId());
/* Now serialize without fill count */
lpIds = reinterpret_cast<lp_id_t*>(localPoolIdBuff);
localPoolIdBuffPtr = localPoolIdBuff;
serSize = 0;
CHECK(localSet.serializeLocalPoolIds(&localPoolIdBuffPtr, &serSize, maxSize,
SerializeIF::Endianness::MACHINE,
false) == retval::CATCH_OK);
CHECK(serSize == maxSize - sizeof(uint8_t));
CHECK(lpIds[0] == localSet.localPoolVarUint8.getDataPoolId());
CHECK(lpIds[1] == localSet.localPoolVarFloat.getDataPoolId());
CHECK(lpIds[2] == localSet.localPoolUint16Vec.getDataPoolId());
{
/* Test read operation. Values should be all zeros */
PoolReadGuard readHelper(&localSet);
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
CHECK(not localSet.isValid());
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(not localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(not localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
CHECK(not localSet.localPoolVarUint8.isValid());
/* Now set new values, commit should be done by read helper automatically */
localSet.localPoolVarUint8 = 232;
localSet.localPoolVarFloat = -2324.322;
localSet.localPoolUint16Vec.value[0] = 232;
localSet.localPoolUint16Vec.value[1] = 23923;
localSet.localPoolUint16Vec.value[2] = 1;
localSet.setValidity(true, true);
}
/* Zero out some values for next test */
localSet.localPoolVarUint8 = 0;
localSet.localPoolVarFloat = 0;
localSet.setAllVariablesReadOnly();
CHECK(localSet.localPoolUint16Vec.getReadWriteMode() == pool_rwm_t::VAR_READ);
CHECK(localSet.localPoolVarUint8.getReadWriteMode() == pool_rwm_t::VAR_READ);
CHECK(localSet.localPoolVarFloat.getReadWriteMode() == pool_rwm_t::VAR_READ);
{
/* Now we read again and check whether our zeroed values were overwritten with
the values in the pool */
PoolReadGuard readHelper(&localSet);
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
CHECK(localSet.isValid());
CHECK(localSet.localPoolVarUint8.value == 232);
CHECK(localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(-2324.322));
CHECK(localSet.localPoolVarFloat.isValid());
CHECK(localSet.localPoolUint16Vec.value[0] == 232);
CHECK(localSet.localPoolUint16Vec.value[1] == 23923);
CHECK(localSet.localPoolUint16Vec.value[2] == 1);
CHECK(localSet.localPoolUint16Vec.isValid());
/* Now we serialize these values into a buffer without the validity buffer */
localSet.setValidityBufferGeneration(false);
maxSize = localSet.getSerializedSize();
CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float));
serSize = 0;
/* Already reserve additional space for validity buffer, will be needed later */
uint8_t buffer[maxSize + 1];
uint8_t* buffPtr = buffer;
CHECK(localSet.serialize(&buffPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
uint8_t rawUint8 = buffer[0];
CHECK(rawUint8 == 232);
float rawFloat = 0.0;
std::memcpy(&rawFloat, buffer + sizeof(uint8_t), sizeof(float));
CHECK(rawFloat == Catch::Approx(-2324.322));
uint16_t rawUint16Vec[3];
std::memcpy(&rawUint16Vec, buffer + sizeof(uint8_t) + sizeof(float), 3 * sizeof(uint16_t));
CHECK(rawUint16Vec[0] == 232);
CHECK(rawUint16Vec[1] == 23923);
CHECK(rawUint16Vec[2] == 1);
size_t sizeToDeserialize = maxSize;
/* Now we zeros out the raw entries and deserialize back into the dataset */
std::memset(buffer, 0, sizeof(buffer));
const uint8_t* constBuffPtr = buffer;
CHECK(localSet.deSerialize(&constBuffPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
/* Check whether deserialization was successfull */
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
/* Validity should be unchanged */
CHECK(localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.isValid());
CHECK(localSet.localPoolUint16Vec.isValid());
/* Now we do the same process but with the validity buffer */
localSet.localPoolVarUint8 = 232;
localSet.localPoolVarFloat = -2324.322;
localSet.localPoolUint16Vec.value[0] = 232;
localSet.localPoolUint16Vec.value[1] = 23923;
localSet.localPoolUint16Vec.value[2] = 1;
localSet.localPoolVarUint8.setValid(true);
localSet.localPoolVarFloat.setValid(false);
localSet.localPoolUint16Vec.setValid(true);
localSet.setValidityBufferGeneration(true);
maxSize = localSet.getSerializedSize();
CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float) + 1);
serSize = 0;
buffPtr = buffer;
CHECK(localSet.serialize(&buffPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
CHECK(rawUint8 == 232);
std::memcpy(&rawFloat, buffer + sizeof(uint8_t), sizeof(float));
CHECK(rawFloat == Catch::Approx(-2324.322));
std::memcpy(&rawUint16Vec, buffer + sizeof(uint8_t) + sizeof(float), 3 * sizeof(uint16_t));
CHECK(rawUint16Vec[0] == 232);
CHECK(rawUint16Vec[1] == 23923);
CHECK(rawUint16Vec[2] == 1);
/* We can do it like this because the buffer only has one byte for
less than 8 variables */
uint8_t* validityByte = buffer + sizeof(buffer) - 1;
bool bitSet = false;
bitutil::get(validityByte, 0, bitSet);
CHECK(bitSet == true);
bitutil::get(validityByte, 1, bitSet);
CHECK(bitSet == false);
bitutil::get(validityByte, 2, bitSet);
CHECK(bitSet == true);
/* Now we manipulate the validity buffer for the deserialization */
bitutil::clear(validityByte, 0);
bitutil::set(validityByte, 1);
bitutil::clear(validityByte, 2);
/* Zero out everything except validity buffer */
std::memset(buffer, 0, sizeof(buffer) - 1);
sizeToDeserialize = maxSize;
constBuffPtr = buffer;
CHECK(localSet.deSerialize(&constBuffPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
/* Check whether deserialization was successfull */
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
CHECK(not localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.isValid());
CHECK(not localSet.localPoolUint16Vec.isValid());
}
/* Common fault test cases */
LocalPoolObjectBase* variableHandle = poolOwner->getPoolObjectHandle(lpool::uint32VarId);
CHECK(variableHandle != nullptr);
CHECK(localSet.registerVariable(variableHandle) == static_cast<int>(DataSetIF::DATA_SET_FULL));
variableHandle = nullptr;
REQUIRE(localSet.registerVariable(variableHandle) ==
static_cast<int>(DataSetIF::POOL_VAR_NULL));
}
SECTION("MorePoolVariables") {
LocalDataSet set(poolOwner, 2, 10);
/* Register same variables again to get more than 8 registered variables */
for (uint8_t idx = 0; idx < 8; idx++) {
REQUIRE(set.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK);
}
REQUIRE(set.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK);
REQUIRE(set.registerVariable(&localSet.localPoolUint16Vec) == retval::CATCH_OK);
set.setValidityBufferGeneration(true);
{
PoolReadGuard readHelper(&localSet);
localSet.localPoolVarUint8.value = 42;
localSet.localPoolVarUint8.setValid(true);
localSet.localPoolUint16Vec.setValid(false);
}
size_t maxSize = set.getSerializedSize();
CHECK(maxSize == 9 + sizeof(uint16_t) * 3 + 2);
size_t serSize = 0;
/* Already reserve additional space for validity buffer, will be needed later */
uint8_t buffer[maxSize + 1];
uint8_t* buffPtr = buffer;
CHECK(set.serialize(&buffPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
std::array<uint8_t, 2> validityBuffer;
std::memcpy(validityBuffer.data(), buffer + 9 + sizeof(uint16_t) * 3, 2);
/* The first 9 variables should be valid */
CHECK(validityBuffer[0] == 0xff);
bool bitSet = false;
bitutil::get(validityBuffer.data() + 1, 0, bitSet);
CHECK(bitSet == true);
bitutil::get(validityBuffer.data() + 1, 1, bitSet);
CHECK(bitSet == false);
/* Now we invert the validity */
validityBuffer[0] = 0;
validityBuffer[1] = 0b0100'0000;
std::memcpy(buffer + 9 + sizeof(uint16_t) * 3, validityBuffer.data(), 2);
const uint8_t* constBuffPtr = buffer;
size_t sizeToDeSerialize = serSize;
CHECK(set.deSerialize(&constBuffPtr, &sizeToDeSerialize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
CHECK(localSet.localPoolVarUint8.isValid() == false);
CHECK(localSet.localPoolUint16Vec.isValid() == true);
}
SECTION("SharedDataSet") {
object_id_t sharedSetId = objects::SHARED_SET_ID;
SharedLocalDataSet sharedSet(sharedSetId, poolOwner, lpool::testSetId, 5);
localSet.localPoolVarUint8.setReadWriteMode(pool_rwm_t::VAR_WRITE);
localSet.localPoolUint16Vec.setReadWriteMode(pool_rwm_t::VAR_WRITE);
CHECK(sharedSet.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK);
CHECK(sharedSet.registerVariable(&localSet.localPoolUint16Vec) == retval::CATCH_OK);
CHECK(sharedSet.initialize() == retval::CATCH_OK);
CHECK(sharedSet.lockDataset() == retval::CATCH_OK);
CHECK(sharedSet.unlockDataset() == retval::CATCH_OK);
{
// PoolReadGuard rg(&sharedSet);
// CHECK(rg.getReadResult() == retval::CATCH_OK);
localSet.localPoolVarUint8.value = 5;
localSet.localPoolUint16Vec.value[0] = 1;
localSet.localPoolUint16Vec.value[1] = 2;
localSet.localPoolUint16Vec.value[2] = 3;
CHECK(sharedSet.commit() == retval::CATCH_OK);
}
sharedSet.setReadCommitProtectionBehaviour(true);
}
/* we need to reset the subscription list because the pool owner
is a global object. */
CHECK(poolOwner->reset() == retval::CATCH_OK);
}

View File

@ -0,0 +1,413 @@
#include <fsfw/datapool/PoolReadGuard.h>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/globalfunctions/timevalOperations.h>
#include <fsfw/housekeeping/HousekeepingSnapshot.h>
#include <fsfw/ipc/CommandMessageCleaner.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/timemanager/CCSDSTime.h>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <iostream>
#include "LocalPoolOwnerBase.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("LocalPoolManagerTest", "[LocManTest]") {
LocalPoolOwnerBase* poolOwner =
ObjectManager::instance()->get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
REQUIRE(poolOwner != nullptr);
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK);
MessageQueueMockBase* poolOwnerMock = poolOwner->getMockQueueHandle();
REQUIRE(poolOwnerMock != nullptr);
// MessageQueueIF* hkCommander = QueueFactory::instance()->createMessageQueue();
CommandMessage messageSent;
uint8_t messagesSent = 0;
SECTION("BasicTest") {
{
/* For code coverage, should not crash */
LocalDataPoolManager manager(nullptr, nullptr);
}
auto owner = poolOwner->poolManager.getOwner();
REQUIRE(owner != nullptr);
CHECK(owner->getObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE);
/* Subscribe for message generation on update. */
REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK);
/* Subscribe for an update message. */
poolOwner->dataset.setChanged(true);
/* Now the update message should be generated. */
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent() == true);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_NOTIFICATION_SET));
/* Should have been reset. */
CHECK(poolOwner->dataset.hasChanged() == false);
/* Set changed again, result should be the same. */
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_NOTIFICATION_SET));
/* Now subscribe for set update HK as well. */
REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 2);
/* first message sent should be the update notification, considering
the internal list is a vector checked in insertion order. */
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_NOTIFICATION_SET));
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(HousekeepingMessage::HK_REPORT));
/* Clear message to avoid memory leak, our mock won't do it for us (yet) */
CommandMessageCleaner::clearCommandMessage(&messageSent);
}
SECTION("SetSnapshotUpdateTest") {
/* Set the variables in the set to certain values. These are checked later. */
{
PoolReadGuard readHelper(&poolOwner->dataset);
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
poolOwner->dataset.localPoolVarUint8.value = 5;
poolOwner->dataset.localPoolVarFloat.value = -12.242;
poolOwner->dataset.localPoolUint16Vec.value[0] = 2;
poolOwner->dataset.localPoolUint16Vec.value[1] = 32;
poolOwner->dataset.localPoolUint16Vec.value[2] = 42932;
}
/* Subscribe for snapshot generation on update. */
REQUIRE(poolOwner->subscribeWrapperSetUpdateSnapshot() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
/* Store current time, we are going to check the (approximate) time equality later */
timeval now;
Clock::getClock_timeval(&now);
/* Trigger generation of snapshot */
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
/* Check that snapshot was generated */
CHECK(messageSent.getCommand() == static_cast<int>(HousekeepingMessage::UPDATE_SNAPSHOT_SET));
/* Now we deserialize the snapshot into a new dataset instance */
CCSDSTime::CDS_short cdsShort;
LocalPoolTestDataSet newSet;
HousekeepingSnapshot snapshot(&cdsShort, &newSet);
store_address_t storeId;
HousekeepingMessage::getUpdateSnapshotSetCommand(&messageSent, &storeId);
ConstAccessorPair accessorPair = tglob::getIpcStoreHandle()->getData(storeId);
REQUIRE(accessorPair.first == retval::CATCH_OK);
const uint8_t* readOnlyPtr = accessorPair.second.data();
size_t sizeToDeserialize = accessorPair.second.size();
CHECK(newSet.localPoolVarFloat.value == 0);
CHECK(newSet.localPoolVarUint8 == 0);
CHECK(newSet.localPoolUint16Vec.value[0] == 0);
CHECK(newSet.localPoolUint16Vec.value[1] == 0);
CHECK(newSet.localPoolUint16Vec.value[2] == 0);
/* Fill the dataset and timestamp */
REQUIRE(snapshot.deSerialize(&readOnlyPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
/* Now we check that the snapshot is actually correct */
CHECK(newSet.localPoolVarFloat.value == Catch::Approx(-12.242));
CHECK(newSet.localPoolVarUint8 == 5);
CHECK(newSet.localPoolUint16Vec.value[0] == 2);
CHECK(newSet.localPoolUint16Vec.value[1] == 32);
CHECK(newSet.localPoolUint16Vec.value[2] == 42932);
/* Now we check that both times are equal */
timeval timeFromHK;
auto result = CCSDSTime::convertFromCDS(&timeFromHK, &cdsShort);
CHECK(result == HasReturnvaluesIF::RETURN_OK);
timeval difference = timeFromHK - now;
CHECK(timevalOperations::toDouble(difference) < 1.0);
}
SECTION("VariableSnapshotTest") {
/* Acquire subscription interface */
ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface();
REQUIRE(subscriptionIF != nullptr);
/* Subscribe for variable snapshot */
REQUIRE(poolOwner->subscribeWrapperVariableSnapshot(lpool::uint8VarId) == retval::CATCH_OK);
auto poolVar =
dynamic_cast<lp_var_t<uint8_t>*>(poolOwner->getPoolObjectHandle(lpool::uint8VarId));
REQUIRE(poolVar != nullptr);
{
PoolReadGuard rg(poolVar);
CHECK(rg.getReadResult() == retval::CATCH_OK);
poolVar->value = 25;
}
poolVar->setChanged(true);
/* Store current time, we are going to check the (approximate) time equality later */
CCSDSTime::CDS_short timeCdsNow;
timeval now;
Clock::getClock_timeval(&now);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
/* Check update snapshot was sent. */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
/* Should have been reset. */
CHECK(poolVar->hasChanged() == false);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE));
/* Now we deserialize the snapshot into a new dataset instance */
CCSDSTime::CDS_short cdsShort;
lp_var_t<uint8_t> varCopy = lp_var_t<uint8_t>(lpool::uint8VarGpid);
HousekeepingSnapshot snapshot(&cdsShort, &varCopy);
store_address_t storeId;
HousekeepingMessage::getUpdateSnapshotVariableCommand(&messageSent, &storeId);
ConstAccessorPair accessorPair = tglob::getIpcStoreHandle()->getData(storeId);
REQUIRE(accessorPair.first == retval::CATCH_OK);
const uint8_t* readOnlyPtr = accessorPair.second.data();
size_t sizeToDeserialize = accessorPair.second.size();
CHECK(varCopy.value == 0);
/* Fill the dataset and timestamp */
REQUIRE(snapshot.deSerialize(&readOnlyPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
CHECK(varCopy.value == 25);
/* Now we check that both times are equal */
timeval timeFromHK;
auto result = CCSDSTime::convertFromCDS(&timeFromHK, &cdsShort);
CHECK(result == HasReturnvaluesIF::RETURN_OK);
timeval difference = timeFromHK - now;
CHECK(timevalOperations::toDouble(difference) < 1.0);
}
SECTION("VariableNotificationTest") {
/* Acquire subscription interface */
ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface();
REQUIRE(subscriptionIF != nullptr);
/* Subscribe for variable update */
REQUIRE(poolOwner->subscribeWrapperVariableUpdate(lpool::uint8VarId) == retval::CATCH_OK);
lp_var_t<uint8_t>* poolVar =
dynamic_cast<lp_var_t<uint8_t>*>(poolOwner->getPoolObjectHandle(lpool::uint8VarId));
REQUIRE(poolVar != nullptr);
poolVar->setChanged(true);
REQUIRE(poolVar->hasChanged() == true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
/* Check update notification was sent. */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
/* Should have been reset. */
CHECK(poolVar->hasChanged() == false);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE));
/* Now subscribe for the dataset update (HK and update) again with subscription interface */
REQUIRE(subscriptionIF->subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT,
objects::HK_RECEIVER_MOCK,
false) == retval::CATCH_OK);
REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
/* Now two messages should be sent. */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 2);
poolOwnerMock->clearMessages(true);
poolOwner->dataset.setChanged(true);
poolVar->setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
/* Now three messages should be sent. */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 3);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE));
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() ==
static_cast<int>(HousekeepingMessage::UPDATE_NOTIFICATION_SET));
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(HousekeepingMessage::HK_REPORT));
CommandMessageCleaner::clearCommandMessage(&messageSent);
REQUIRE(poolOwnerMock->receiveMessage(&messageSent) == static_cast<int>(MessageQueueIF::EMPTY));
}
SECTION("PeriodicHKAndMessaging") {
/* Now we subcribe for a HK periodic generation. Even when it's difficult to simulate
the temporal behaviour correctly the HK manager should generate a HK packet
immediately and the periodic helper depends on HK op function calls anyway instead of
using the clock, so we could also just call performHkOperation multiple times */
REQUIRE(poolOwner->subscribePeriodicHk(true) == retval::CATCH_OK);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
/* Now HK packet should be sent as message immediately. */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
LocalPoolDataSetBase* setHandle = poolOwner->getDataSetHandle(lpool::testSid);
REQUIRE(setHandle != nullptr);
CHECK(poolOwner->poolManager.generateHousekeepingPacket(lpool::testSid, setHandle, false) ==
retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
CHECK(setHandle->getReportingEnabled() == true);
CommandMessage hkCmd;
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(setHandle->getReportingEnabled() == false);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, true, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(setHandle->getReportingEnabled() == true);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(setHandle->getReportingEnabled() == false);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4,
false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
/* For non-diagnostics and a specified minimum frequency of 0.2 seconds, the
resulting collection interval should be 1.0 second */
CHECK(poolOwner->dataset.getCollectionInterval() == 1.0);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
/* Now HK packet should be sent as message. */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setUpdateNotificationSetCommand(&hkCmd, lpool::testSid);
sid_t sidToCheck;
store_address_t storeId;
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(poolOwner->changedDataSetCallbackWasCalled(sidToCheck, storeId) == true);
CHECK(sidToCheck == lpool::testSid);
/* Now we test the handling is the dataset is set to diagnostic */
poolOwner->dataset.setDiagnostic(true);
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
/* We still expect a failure message being sent */
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4,
false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, true);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4,
true);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, true, true);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, true);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, false);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, true);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
REQUIRE(poolOwnerMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
CHECK(poolOwnerMock->popMessage() == retval::CATCH_OK);
HousekeepingMessage::setUpdateNotificationVariableCommand(&hkCmd, lpool::uint8VarGpid);
gp_id_t gpidToCheck;
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(poolOwner->changedVariableCallbackWasCalled(gpidToCheck, storeId) == true);
CHECK(gpidToCheck == lpool::uint8VarGpid);
HousekeepingMessage::setUpdateSnapshotSetCommand(&hkCmd, lpool::testSid,
storeId::INVALID_STORE_ADDRESS);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(poolOwner->changedDataSetCallbackWasCalled(sidToCheck, storeId) == true);
CHECK(sidToCheck == lpool::testSid);
HousekeepingMessage::setUpdateSnapshotVariableCommand(&hkCmd, lpool::uint8VarGpid,
storeId::INVALID_STORE_ADDRESS);
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
CHECK(poolOwner->changedVariableCallbackWasCalled(gpidToCheck, storeId) == true);
CHECK(gpidToCheck == lpool::uint8VarGpid);
poolOwner->poolManager.printPoolEntry(lpool::uint8VarId);
}
/* we need to reset the subscription list because the pool owner
is a global object. */
CHECK(poolOwner->reset() == retval::CATCH_OK);
poolOwnerMock->clearMessages(true);
}

View File

@ -0,0 +1,127 @@
#include "LocalPoolOwnerBase.h"
LocalPoolOwnerBase::LocalPoolOwnerBase(object_id_t objectId)
: SystemObject(objectId), poolManager(this, messageQueue), dataset(this, lpool::testSetId) {
messageQueue = new MessageQueueMockBase();
}
LocalPoolOwnerBase::~LocalPoolOwnerBase() {
QueueFactory::instance()->deleteMessageQueue(messageQueue);
}
ReturnValue_t LocalPoolOwnerBase::initializeHkManager() {
if (not initialized) {
initialized = true;
return poolManager.initialize(messageQueue);
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t LocalPoolOwnerBase::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
// Default initialization empty for now.
localDataPoolMap.emplace(lpool::uint8VarId, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(lpool::floatVarId, new PoolEntry<float>({0}));
localDataPoolMap.emplace(lpool::uint32VarId, new PoolEntry<uint32_t>({0}));
localDataPoolMap.emplace(lpool::uint16Vec3Id, new PoolEntry<uint16_t>({0, 0, 0}));
localDataPoolMap.emplace(lpool::int64Vec2Id, new PoolEntry<int64_t>({0, 0}));
return HasReturnvaluesIF::RETURN_OK;
}
LocalPoolObjectBase *LocalPoolOwnerBase::getPoolObjectHandle(lp_id_t localPoolId) {
if (localPoolId == lpool::uint8VarId) {
return &testUint8;
} else if (localPoolId == lpool::uint16Vec3Id) {
return &testUint16Vec;
} else if (localPoolId == lpool::floatVarId) {
return &testFloat;
} else if (localPoolId == lpool::int64Vec2Id) {
return &testInt64Vec;
} else if (localPoolId == lpool::uint32VarId) {
return &testUint32;
} else {
return &testUint8;
}
}
ReturnValue_t LocalPoolOwnerBase::reset() {
resetSubscriptionList();
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
{
PoolReadGuard readHelper(&dataset);
if (readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult();
}
dataset.localPoolVarUint8.value = 0;
dataset.localPoolVarFloat.value = 0.0;
dataset.localPoolUint16Vec.value[0] = 0;
dataset.localPoolUint16Vec.value[1] = 0;
dataset.localPoolUint16Vec.value[2] = 0;
dataset.setValidity(false, true);
}
{
PoolReadGuard readHelper(&testUint32);
if (readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult();
}
testUint32.value = 0;
testUint32.setValid(false);
}
{
PoolReadGuard readHelper(&testInt64Vec);
if (readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult();
}
testInt64Vec.value[0] = 0;
testInt64Vec.value[1] = 0;
testInt64Vec.setValid(false);
}
return status;
}
bool LocalPoolOwnerBase::changedDataSetCallbackWasCalled(sid_t &sid, store_address_t &storeId) {
bool condition = false;
if (not this->changedDatasetSid.notSet()) {
condition = true;
}
sid = changedDatasetSid;
storeId = storeIdForChangedSet;
this->changedDatasetSid.raw = sid_t::INVALID_SID;
this->storeIdForChangedSet = storeId::INVALID_STORE_ADDRESS;
return condition;
}
void LocalPoolOwnerBase::handleChangedDataset(sid_t sid, store_address_t storeId,
bool *clearMessage) {
this->changedDatasetSid = sid;
this->storeIdForChangedSet = storeId;
}
bool LocalPoolOwnerBase::changedVariableCallbackWasCalled(gp_id_t &gpid, store_address_t &storeId) {
bool condition = false;
if (not this->changedPoolVariableGpid.notSet()) {
condition = true;
}
gpid = changedPoolVariableGpid;
storeId = storeIdForChangedVariable;
this->changedPoolVariableGpid.raw = gp_id_t::INVALID_GPID;
this->storeIdForChangedVariable = storeId::INVALID_STORE_ADDRESS;
return condition;
}
ReturnValue_t LocalPoolOwnerBase::initializeHkManagerAfterTaskCreation() {
if (not initializedAfterTaskCreation) {
initializedAfterTaskCreation = true;
return poolManager.initializeAfterTaskCreation();
}
return HasReturnvaluesIF::RETURN_OK;
}
void LocalPoolOwnerBase::handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId,
bool *clearMessage) {
this->changedPoolVariableGpid = globPoolId;
this->storeIdForChangedVariable = storeId;
}

View File

@ -0,0 +1,164 @@
#ifndef FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_
#define FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_
#include <fsfw/datapool/PoolReadGuard.h>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/LocalDataSet.h>
#include <fsfw/datapoollocal/LocalPoolVariable.h>
#include <fsfw/datapoollocal/LocalPoolVector.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/objectmanager/SystemObject.h>
#include "../mocks/MessageQueueMockBase.h"
#include "tests/TestsConfig.h"
namespace lpool {
static constexpr lp_id_t uint8VarId = 0;
static constexpr lp_id_t floatVarId = 1;
static constexpr lp_id_t uint32VarId = 2;
static constexpr lp_id_t uint16Vec3Id = 3;
static constexpr lp_id_t int64Vec2Id = 4;
static constexpr uint32_t testSetId = 0;
static constexpr uint8_t dataSetMaxVariables = 10;
static const sid_t testSid = sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, testSetId);
static const gp_id_t uint8VarGpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, uint8VarId);
static const gp_id_t floatVarGpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, floatVarId);
static const gp_id_t uint32Gpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, uint32VarId);
static const gp_id_t uint16Vec3Gpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, uint16Vec3Id);
static const gp_id_t uint64Vec2Id = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, int64Vec2Id);
} // namespace lpool
class LocalPoolStaticTestDataSet : public StaticLocalDataSet<3> {
public:
LocalPoolStaticTestDataSet() : StaticLocalDataSet(lpool::testSid) {}
LocalPoolStaticTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId)
: StaticLocalDataSet(owner, setId) {}
lp_var_t<uint8_t> localPoolVarUint8 = lp_var_t<uint8_t>(lpool::uint8VarGpid, this);
lp_var_t<float> localPoolVarFloat = lp_var_t<float>(lpool::floatVarGpid, this);
lp_vec_t<uint16_t, 3> localPoolUint16Vec = lp_vec_t<uint16_t, 3>(lpool::uint16Vec3Gpid, this);
private:
};
class LocalPoolTestDataSet : public LocalDataSet {
public:
LocalPoolTestDataSet() : LocalDataSet(lpool::testSid, lpool::dataSetMaxVariables) {}
LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId)
: LocalDataSet(owner, setId, lpool::dataSetMaxVariables) {}
lp_var_t<uint8_t> localPoolVarUint8 = lp_var_t<uint8_t>(lpool::uint8VarGpid, this);
lp_var_t<float> localPoolVarFloat = lp_var_t<float>(lpool::floatVarGpid, this);
lp_vec_t<uint16_t, 3> localPoolUint16Vec = lp_vec_t<uint16_t, 3>(lpool::uint16Vec3Gpid, this);
void setDiagnostic(bool isDiagnostic) { LocalPoolDataSetBase::setDiagnostic(isDiagnostic); }
private:
};
class LocalPoolOwnerBase : public SystemObject, public HasLocalDataPoolIF {
public:
LocalPoolOwnerBase(object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE);
~LocalPoolOwnerBase();
object_id_t getObjectId() const override { return SystemObject::getObjectId(); }
ReturnValue_t initializeHkManager();
ReturnValue_t initializeHkManagerAfterTaskCreation();
/** Command queue for housekeeping messages. */
MessageQueueId_t getCommandQueue() const override { return messageQueue->getId(); }
// This is called by initializeAfterTaskCreation of the HK manager.
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
LocalDataPoolManager* getHkManagerHandle() override { return &poolManager; }
dur_millis_t getPeriodicOperationFrequency() const override { return 200; }
/**
* This function is used by the pool manager to get a valid dataset
* from a SID
* @param sid Corresponding structure ID
* @return
*/
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override { return &dataset; }
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) override;
MessageQueueMockBase* getMockQueueHandle() const {
return dynamic_cast<MessageQueueMockBase*>(messageQueue);
}
ReturnValue_t subscribePeriodicHk(bool enableReporting) {
return poolManager.subscribeForPeriodicPacket(lpool::testSid, enableReporting, 0.2, false);
}
ReturnValue_t subscribeWrapperSetUpdate() {
return poolManager.subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT,
objects::HK_RECEIVER_MOCK, false);
}
ReturnValue_t subscribeWrapperSetUpdateSnapshot() {
return poolManager.subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT,
objects::HK_RECEIVER_MOCK, true);
}
ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) {
return poolManager.subscribeForUpdatePacket(lpool::testSid, diagnostics, false,
objects::HK_RECEIVER_MOCK);
}
ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) {
return poolManager.subscribeForVariableUpdateMessage(localPoolId, MessageQueueIF::NO_QUEUE,
objects::HK_RECEIVER_MOCK, false);
}
ReturnValue_t subscribeWrapperVariableSnapshot(lp_id_t localPoolId) {
return poolManager.subscribeForVariableUpdateMessage(localPoolId, MessageQueueIF::NO_QUEUE,
objects::HK_RECEIVER_MOCK, true);
}
ReturnValue_t reset();
void resetSubscriptionList() { poolManager.clearReceiversList(); }
bool changedDataSetCallbackWasCalled(sid_t& sid, store_address_t& storeId);
bool changedVariableCallbackWasCalled(gp_id_t& gpid, store_address_t& storeId);
LocalDataPoolManager poolManager;
LocalPoolTestDataSet dataset;
private:
void handleChangedDataset(sid_t sid, store_address_t storeId, bool* clearMessage) override;
sid_t changedDatasetSid;
store_address_t storeIdForChangedSet;
void handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId,
bool* clearMessage) override;
gp_id_t changedPoolVariableGpid;
store_address_t storeIdForChangedVariable;
lp_var_t<uint8_t> testUint8 = lp_var_t<uint8_t>(this, lpool::uint8VarId);
lp_var_t<float> testFloat = lp_var_t<float>(this, lpool::floatVarId);
lp_var_t<uint32_t> testUint32 = lp_var_t<uint32_t>(this, lpool::uint32VarId);
lp_vec_t<uint16_t, 3> testUint16Vec = lp_vec_t<uint16_t, 3>(this, lpool::uint16Vec3Id);
lp_vec_t<int64_t, 2> testInt64Vec = lp_vec_t<int64_t, 2>(this, lpool::int64Vec2Id);
MessageQueueIF* messageQueue = nullptr;
bool initialized = false;
bool initializedAfterTaskCreation = false;
};
#endif /* FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ */

View File

@ -0,0 +1,111 @@
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <catch2/catch_test_macros.hpp>
#include "LocalPoolOwnerBase.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
#include "tests/TestsConfig.h"
TEST_CASE("LocalPoolVariable", "[LocPoolVarTest]") {
LocalPoolOwnerBase* poolOwner =
ObjectManager::instance()->get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
REQUIRE(poolOwner != nullptr);
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK);
SECTION("Basic Tests") {
/* very basic test. */
lp_var_t<uint8_t> testVariable =
lp_var_t<uint8_t>(objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId);
REQUIRE(testVariable.read() == retval::CATCH_OK);
CHECK(testVariable.value == 0);
testVariable.value = 5;
REQUIRE(testVariable.commit() == retval::CATCH_OK);
REQUIRE(testVariable.read() == retval::CATCH_OK);
REQUIRE(testVariable.value == 5);
CHECK(not testVariable.isValid());
testVariable.setValid(true);
CHECK(testVariable.isValid());
CHECK(testVariable.commit(true) == retval::CATCH_OK);
testVariable.setReadWriteMode(pool_rwm_t::VAR_READ);
CHECK(testVariable.getReadWriteMode() == pool_rwm_t::VAR_READ);
testVariable.setReadWriteMode(pool_rwm_t::VAR_READ_WRITE);
testVariable.setDataPoolId(22);
CHECK(testVariable.getDataPoolId() == 22);
testVariable.setDataPoolId(lpool::uint8VarId);
testVariable.setChanged(true);
CHECK(testVariable.hasChanged());
testVariable.setChanged(false);
gp_id_t globPoolId(objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId);
lp_var_t<uint8_t> testVariable2 = lp_var_t<uint8_t>(globPoolId);
REQUIRE(testVariable2.read() == retval::CATCH_OK);
CHECK(testVariable2 == 5);
CHECK(testVariable == testVariable2);
testVariable = 10;
CHECK(testVariable != 5);
// CHECK(not testVariable != testVariable2);
uint8_t variableRaw = 0;
uint8_t* varPtr = &variableRaw;
size_t maxSize = testVariable.getSerializedSize();
CHECK(maxSize == 1);
size_t serSize = 0;
CHECK(testVariable.serialize(&varPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
CHECK(variableRaw == 10);
const uint8_t* varConstPtr = &variableRaw;
testVariable = 5;
CHECK(testVariable.deSerialize(&varConstPtr, &serSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
CHECK(testVariable == 10);
CHECK(testVariable != testVariable2);
CHECK(testVariable2 < testVariable);
CHECK(testVariable2 < 10);
CHECK(testVariable > 5);
CHECK(testVariable > testVariable2);
variableRaw = static_cast<uint8_t>(testVariable2);
CHECK(variableRaw == 5);
CHECK(testVariable == 10);
testVariable = testVariable2;
CHECK(testVariable == 5);
}
SECTION("ErrorHandling") {
/* now try to use a local pool variable which does not exist */
lp_var_t<uint8_t> invalidVariable =
lp_var_t<uint8_t>(objects::TEST_LOCAL_POOL_OWNER_BASE, 0xffffffff);
REQUIRE(invalidVariable.read() == static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
REQUIRE(invalidVariable.commit() == static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
/* now try to access with wrong type */
lp_var_t<int8_t> invalidVariable2 =
lp_var_t<int8_t>(objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId);
REQUIRE(invalidVariable2.read() == static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
lp_var_t<uint8_t> readOnlyVar = lp_var_t<uint8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId, nullptr, pool_rwm_t::VAR_READ);
REQUIRE(readOnlyVar.commit() == static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
lp_var_t<uint8_t> writeOnlyVar = lp_var_t<uint8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId, nullptr, pool_rwm_t::VAR_WRITE);
REQUIRE(writeOnlyVar.read() == static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
lp_var_t<uint32_t> uint32tVar =
lp_var_t<uint32_t>(objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint32VarId);
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "LocalPoolVariable printout: " << uint32tVar << std::endl;
#endif
/* for code coverage. If program does not crash -> OK */
lp_var_t<uint8_t> invalidObjectVar = lp_var_t<uint8_t>(0xffffffff, lpool::uint8VarId);
gp_id_t globPoolId(0xffffffff, lpool::uint8VarId);
lp_var_t<uint8_t> invalidObjectVar2 = lp_var_t<uint8_t>(globPoolId);
lp_var_t<uint8_t> invalidObjectVar3 = lp_var_t<uint8_t>(nullptr, lpool::uint8VarId);
}
CHECK(poolOwner->reset() == retval::CATCH_OK);
}

View File

@ -0,0 +1,110 @@
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <catch2/catch_test_macros.hpp>
#include "LocalPoolOwnerBase.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
#include "tests/TestsConfig.h"
TEST_CASE("LocalPoolVector", "[LocPoolVecTest]") {
LocalPoolOwnerBase* poolOwner =
ObjectManager::instance()->get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
REQUIRE(poolOwner != nullptr);
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK);
SECTION("BasicTest") {
// very basic test.
lp_vec_t<uint16_t, 3> testVector =
lp_vec_t<uint16_t, 3>(objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id);
REQUIRE(testVector.read() == retval::CATCH_OK);
testVector.value[0] = 5;
testVector.value[1] = 232;
testVector.value[2] = 32023;
REQUIRE(testVector.commit(true) == retval::CATCH_OK);
CHECK(testVector.isValid());
testVector.value[0] = 0;
testVector.value[1] = 0;
testVector.value[2] = 0;
CHECK(testVector.read() == retval::CATCH_OK);
CHECK(testVector.value[0] == 5);
CHECK(testVector.value[1] == 232);
CHECK(testVector.value[2] == 32023);
CHECK(testVector[0] == 5);
/* This is invalid access, so the last value will be set instead.
(we can't throw exceptions) */
testVector[4] = 12;
CHECK(testVector[2] == 12);
CHECK(testVector.commit() == retval::CATCH_OK);
/* Use read-only reference. */
const lp_vec_t<uint16_t, 3>& roTestVec = testVector;
uint16_t valueOne = roTestVec[0];
CHECK(valueOne == 5);
uint16_t lastVal = roTestVec[25];
CHECK(lastVal == 12);
size_t maxSize = testVector.getSerializedSize();
CHECK(maxSize == 6);
uint16_t serializedVector[3];
uint8_t* vecPtr = reinterpret_cast<uint8_t*>(serializedVector);
size_t serSize = 0;
REQUIRE(testVector.serialize(&vecPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
CHECK(serSize == 6);
CHECK(serializedVector[0] == 5);
CHECK(serializedVector[1] == 232);
CHECK(serializedVector[2] == 12);
maxSize = 1;
REQUIRE(testVector.serialize(&vecPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) ==
static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
serializedVector[0] = 16;
serializedVector[1] = 7832;
serializedVector[2] = 39232;
const uint8_t* constVecPtr = reinterpret_cast<const uint8_t*>(serializedVector);
REQUIRE(testVector.deSerialize(&constVecPtr, &serSize, SerializeIF::Endianness::MACHINE) ==
retval::CATCH_OK);
CHECK(testVector[0] == 16);
CHECK(testVector[1] == 7832);
CHECK(testVector[2] == 39232);
serSize = 1;
REQUIRE(testVector.deSerialize(&constVecPtr, &serSize, SerializeIF::Endianness::MACHINE) ==
static_cast<int>(SerializeIF::STREAM_TOO_SHORT));
}
SECTION("ErrorHandling") {
/* Now try to use a local pool variable which does not exist */
lp_vec_t<uint16_t, 3> invalidVector =
lp_vec_t<uint16_t, 3>(objects::TEST_LOCAL_POOL_OWNER_BASE, 0xffffffff);
REQUIRE(invalidVector.read() == static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
REQUIRE(invalidVector.commit() == static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
/* Now try to access with wrong type */
lp_vec_t<uint32_t, 3> invalidVector2 =
lp_vec_t<uint32_t, 3>(objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id);
REQUIRE(invalidVector2.read() == static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
REQUIRE(invalidVector2.commit() == static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
lp_vec_t<uint16_t, 3> writeOnlyVec = lp_vec_t<uint16_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id, nullptr, pool_rwm_t::VAR_WRITE);
REQUIRE(writeOnlyVec.read() == static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
lp_vec_t<uint16_t, 3> readOnlyVec = lp_vec_t<uint16_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id, nullptr, pool_rwm_t::VAR_READ);
REQUIRE(readOnlyVec.commit() == static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
}
poolOwner->reset();
}

View File

@ -0,0 +1,8 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
CookieIFMock.cpp
ComIFMock.cpp
DeviceHandlerCommander.cpp
DeviceHandlerMock.cpp
DeviceFdirMock.cpp
TestDeviceHandlerBase.cpp
)

View File

@ -0,0 +1,46 @@
#include "ComIFMock.h"
#include "DeviceHandlerMock.h"
ComIFMock::ComIFMock(object_id_t objectId) : SystemObject(objectId) {}
ComIFMock::~ComIFMock() {}
ReturnValue_t ComIFMock::initializeInterface(CookieIF *cookie) { return RETURN_OK; }
ReturnValue_t ComIFMock::sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) {
data = *sendData;
return RETURN_OK;
}
ReturnValue_t ComIFMock::getSendSuccess(CookieIF *cookie) { return RETURN_OK; }
ReturnValue_t ComIFMock::requestReceiveMessage(CookieIF *cookie, size_t requestLen) {
return RETURN_OK;
}
ReturnValue_t ComIFMock::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) {
switch (testCase) {
case TestCase::MISSED_REPLY: {
*size = 0;
return RETURN_OK;
}
case TestCase::SIMPLE_COMMAND_NOMINAL: {
*size = 1;
data = DeviceHandlerMock::SIMPLE_COMMAND_DATA;
*buffer = &data;
break;
}
case TestCase::PERIODIC_REPLY_NOMINAL: {
*size = 1;
data = DeviceHandlerMock::PERIODIC_REPLY_DATA;
*buffer = &data;
break;
}
default:
break;
}
return RETURN_OK;
}
void ComIFMock::setTestCase(TestCase testCase_) { testCase = testCase_; }

View File

@ -0,0 +1,37 @@
#ifndef TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_COMIFMOCK_H_
#define TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_COMIFMOCK_H_
#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
#include <fsfw/objectmanager/SystemObject.h>
/**
* @brief The ComIFMock supports the simulation of various device communication error cases
* like incomplete or wrong replies and can be used to test the
* DeviceHandlerBase.
*/
class ComIFMock : public DeviceCommunicationIF, public SystemObject {
public:
enum class TestCase { SIMPLE_COMMAND_NOMINAL, PERIODIC_REPLY_NOMINAL, MISSED_REPLY };
ComIFMock(object_id_t objectId);
virtual ~ComIFMock();
virtual ReturnValue_t initializeInterface(CookieIF *cookie) override;
virtual ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t *sendData,
size_t sendLen) override;
virtual ReturnValue_t getSendSuccess(CookieIF *cookie) override;
virtual ReturnValue_t requestReceiveMessage(CookieIF *cookie, size_t requestLen) override;
virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer,
size_t *size) override;
void setTestCase(TestCase testCase_);
private:
TestCase testCase = TestCase::SIMPLE_COMMAND_NOMINAL;
static const uint8_t SIMPLE_COMMAND_DATA = 1;
static const uint8_t PERIODIC_REPLY_DATA = 2;
uint8_t data = 0;
};
#endif /* TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_COMIFMOCK_H_ */

View File

@ -0,0 +1,5 @@
#include "CookieIFMock.h"
CookieIFMock::CookieIFMock() {}
CookieIFMock::~CookieIFMock() {}

View File

@ -0,0 +1,12 @@
#ifndef TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_COOKIEIFMOCK_H_
#define TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_COOKIEIFMOCK_H_
#include "fsfw/devicehandlers/CookieIF.h"
class CookieIFMock : public CookieIF {
public:
CookieIFMock();
virtual ~CookieIFMock();
};
#endif /* TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_COOKIEIFMOCK_H_ */

View File

@ -0,0 +1,18 @@
#include "DeviceFdirMock.h"
#include <tests/src/fsfw_tests/unit/devicehandler/DeviceFdirMock.h>
DeviceFdirMock::DeviceFdirMock(object_id_t owner, object_id_t parent)
: DeviceHandlerFailureIsolation(owner, parent) {}
DeviceFdirMock::~DeviceFdirMock() {}
uint32_t DeviceFdirMock::getMissedReplyCount() {
ParameterWrapper parameterWrapper;
this->getParameter(MISSED_REPLY_DOMAIN_ID,
static_cast<uint8_t>(FaultCounter::ParameterIds::FAULT_COUNT),
&parameterWrapper, nullptr, 0);
uint32_t missedReplyCount = 0;
parameterWrapper.getElement(&missedReplyCount);
return missedReplyCount;
}

View File

@ -0,0 +1,18 @@
#ifndef TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEFDIRMOCK_H_
#define TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEFDIRMOCK_H_
#include "fsfw/devicehandlers/DeviceHandlerFailureIsolation.h"
class DeviceFdirMock : public DeviceHandlerFailureIsolation {
public:
DeviceFdirMock(object_id_t owner, object_id_t parent);
virtual ~DeviceFdirMock();
uint32_t getMissedReplyCount();
private:
static const uint8_t STRANGE_REPLY_DOMAIN_ID = 0xF0;
static const uint8_t MISSED_REPLY_DOMAIN_ID = 0xF1;
};
#endif /* TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEFDIRMOCK_H_ */

View File

@ -0,0 +1,64 @@
#include "DeviceHandlerCommander.h"
#include <fsfw/ipc/QueueFactory.h>
DeviceHandlerCommander::DeviceHandlerCommander(object_id_t objectId)
: SystemObject(objectId), commandActionHelper(this) {
auto mqArgs = MqArgs(this->getObjectId());
commandQueue = QueueFactory::instance()->createMessageQueue(
QUEUE_SIZE, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
}
DeviceHandlerCommander::~DeviceHandlerCommander() {}
ReturnValue_t DeviceHandlerCommander::performOperation(uint8_t operationCode) {
readCommandQueue();
return RETURN_OK;
}
ReturnValue_t DeviceHandlerCommander::initialize() {
ReturnValue_t result = commandActionHelper.initialize();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
return HasReturnvaluesIF::RETURN_OK;
}
MessageQueueIF* DeviceHandlerCommander::getCommandQueuePtr() { return commandQueue; }
void DeviceHandlerCommander::stepSuccessfulReceived(ActionId_t actionId, uint8_t step) {}
void DeviceHandlerCommander::stepFailedReceived(ActionId_t actionId, uint8_t step,
ReturnValue_t returnCode) {}
void DeviceHandlerCommander::dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) {
}
void DeviceHandlerCommander::completionSuccessfulReceived(ActionId_t actionId) {
lastReplyReturnCode = RETURN_OK;
}
void DeviceHandlerCommander::completionFailedReceived(ActionId_t actionId,
ReturnValue_t returnCode) {
lastReplyReturnCode = returnCode;
}
void DeviceHandlerCommander::readCommandQueue() {
CommandMessage message;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
for (result = commandQueue->receiveMessage(&message); result == HasReturnvaluesIF::RETURN_OK;
result = commandQueue->receiveMessage(&message)) {
result = commandActionHelper.handleReply(&message);
if (result == HasReturnvaluesIF::RETURN_OK) {
continue;
}
}
}
ReturnValue_t DeviceHandlerCommander::sendCommand(object_id_t target, ActionId_t actionId) {
return commandActionHelper.commandAction(target, actionId, nullptr, 0);
}
ReturnValue_t DeviceHandlerCommander::getReplyReturnCode() { return lastReplyReturnCode; }
void DeviceHandlerCommander::resetReplyReturnCode() { lastReplyReturnCode = RETURN_FAILED; }

View File

@ -0,0 +1,50 @@
#ifndef TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEHANDLERCOMMANDER_H_
#define TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEHANDLERCOMMANDER_H_
#include "fsfw/action/CommandActionHelper.h"
#include "fsfw/action/CommandsActionsIF.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
class DeviceHandlerCommander : public ExecutableObjectIF,
public SystemObject,
public CommandsActionsIF,
public HasReturnvaluesIF {
public:
DeviceHandlerCommander(object_id_t objectId);
virtual ~DeviceHandlerCommander();
ReturnValue_t performOperation(uint8_t operationCode = 0);
ReturnValue_t initialize() override;
MessageQueueIF* getCommandQueuePtr() override;
void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) override;
void stepFailedReceived(ActionId_t actionId, uint8_t step, ReturnValue_t returnCode) override;
void dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) override;
void completionSuccessfulReceived(ActionId_t actionId) override;
void completionFailedReceived(ActionId_t actionId, ReturnValue_t returnCode) override;
/**
* @brief Calling this function will send the command to the device handler object.
*
* @param target Object ID of the device handler
* @param actionId Action ID of the command to send
*/
ReturnValue_t sendCommand(object_id_t target, ActionId_t actionId);
ReturnValue_t getReplyReturnCode();
void resetReplyReturnCode();
private:
static const uint32_t QUEUE_SIZE = 20;
MessageQueueIF* commandQueue = nullptr;
CommandActionHelper commandActionHelper;
ReturnValue_t lastReplyReturnCode = RETURN_FAILED;
void readCommandQueue();
};
#endif /* TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEHANDLERCOMMANDER_H_ */

View File

@ -0,0 +1,103 @@
#include "DeviceHandlerMock.h"
#include <catch2/catch_test_macros.hpp>
DeviceHandlerMock::DeviceHandlerMock(object_id_t objectId, object_id_t deviceCommunication,
CookieIF *comCookie, FailureIsolationBase *fdirInstance)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie, fdirInstance) {
mode = MODE_ON;
}
DeviceHandlerMock::~DeviceHandlerMock() {}
void DeviceHandlerMock::doStartUp() { setMode(_MODE_TO_ON); }
void DeviceHandlerMock::doShutDown() { setMode(_MODE_POWER_DOWN); }
ReturnValue_t DeviceHandlerMock::buildNormalDeviceCommand(DeviceCommandId_t *id) {
return NOTHING_TO_SEND;
}
ReturnValue_t DeviceHandlerMock::buildTransitionDeviceCommand(DeviceCommandId_t *id) {
return NOTHING_TO_SEND;
}
ReturnValue_t DeviceHandlerMock::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t *commandData,
size_t commandDataLen) {
switch (deviceCommand) {
case SIMPLE_COMMAND: {
commandBuffer[0] = SIMPLE_COMMAND_DATA;
rawPacket = commandBuffer;
rawPacketLen = sizeof(SIMPLE_COMMAND_DATA);
break;
}
default:
WARN("DeviceHandlerMock::buildCommandFromCommand: Invalid device command");
break;
}
return RETURN_OK;
}
ReturnValue_t DeviceHandlerMock::scanForReply(const uint8_t *start, size_t len,
DeviceCommandId_t *foundId, size_t *foundLen) {
switch (*start) {
case SIMPLE_COMMAND_DATA: {
*foundId = SIMPLE_COMMAND;
*foundLen = sizeof(SIMPLE_COMMAND_DATA);
return RETURN_OK;
break;
}
case PERIODIC_REPLY_DATA: {
*foundId = PERIODIC_REPLY;
*foundLen = sizeof(PERIODIC_REPLY_DATA);
return RETURN_OK;
break;
}
default:
break;
}
return RETURN_FAILED;
}
ReturnValue_t DeviceHandlerMock::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) {
switch (id) {
case SIMPLE_COMMAND:
case PERIODIC_REPLY: {
periodicReplyReceived = true;
break;
}
default:
break;
}
return RETURN_OK;
}
void DeviceHandlerMock::fillCommandAndReplyMap() {
insertInCommandAndReplyMap(SIMPLE_COMMAND, 0, nullptr, 0, false, false, 0,
&simpleCommandReplyTimeout);
insertInCommandAndReplyMap(PERIODIC_REPLY, 0, nullptr, 0, true, false, 0,
&periodicReplyCountdown);
}
uint32_t DeviceHandlerMock::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 500; }
void DeviceHandlerMock::changePeriodicReplyCountdown(uint32_t timeout) {
periodicReplyCountdown.setTimeout(timeout);
}
void DeviceHandlerMock::changeSimpleCommandReplyCountdown(uint32_t timeout) {
simpleCommandReplyTimeout.setTimeout(timeout);
}
void DeviceHandlerMock::resetPeriodicReplyState() { periodicReplyReceived = false; }
bool DeviceHandlerMock::getPeriodicReplyReceived() { return periodicReplyReceived; }
ReturnValue_t DeviceHandlerMock::enablePeriodicReply(DeviceCommandId_t replyId) {
return updatePeriodicReply(true, replyId);
}
ReturnValue_t DeviceHandlerMock::disablePeriodicReply(DeviceCommandId_t replyId) {
return updatePeriodicReply(false, replyId);
}

View File

@ -0,0 +1,46 @@
#ifndef TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEHANDLERMOCK_H_
#define TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEHANDLERMOCK_H_
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
class DeviceHandlerMock : public DeviceHandlerBase {
public:
static const DeviceCommandId_t SIMPLE_COMMAND = 1;
static const DeviceCommandId_t PERIODIC_REPLY = 2;
static const uint8_t SIMPLE_COMMAND_DATA = 1;
static const uint8_t PERIODIC_REPLY_DATA = 2;
DeviceHandlerMock(object_id_t objectId, object_id_t deviceCommunication, CookieIF *comCookie,
FailureIsolationBase *fdirInstance);
virtual ~DeviceHandlerMock();
void changePeriodicReplyCountdown(uint32_t timeout);
void changeSimpleCommandReplyCountdown(uint32_t timeout);
void resetPeriodicReplyState();
bool getPeriodicReplyReceived();
ReturnValue_t enablePeriodicReply(DeviceCommandId_t replyId);
ReturnValue_t disablePeriodicReply(DeviceCommandId_t replyId);
protected:
void doStartUp() override;
void doShutDown() override;
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override;
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData,
size_t commandDataLen) override;
ReturnValue_t scanForReply(const uint8_t *start, size_t len, DeviceCommandId_t *foundId,
size_t *foundLen) override;
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
void fillCommandAndReplyMap() override;
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
private:
Countdown simpleCommandReplyTimeout = Countdown(1000);
Countdown periodicReplyCountdown = Countdown(1000);
uint8_t commandBuffer[1];
bool periodicReplyReceived = false;
};
#endif /* TESTS_SRC_FSFW_TESTS_UNIT_DEVICEHANDLER_DEVICEHANDLERMOCK_H_ */

View File

@ -0,0 +1,95 @@
#include <catch2/catch_test_macros.hpp>
#include "ComIFMock.h"
#include "DeviceFdirMock.h"
#include "fsfw_tests/unit/devicehandler/CookieIFMock.h"
#include "fsfw_tests/unit/devicehandler/DeviceHandlerCommander.h"
#include "fsfw_tests/unit/devicehandler/DeviceHandlerMock.h"
#include "fsfw_tests/unit/testcfg/objects/systemObjectList.h"
TEST_CASE("Device Handler Base", "[DeviceHandlerBase]") {
// Will be deleted with DHB destructor
CookieIFMock* cookieIFMock = new CookieIFMock;
ComIFMock comIF(objects::COM_IF_MOCK);
DeviceFdirMock deviceFdirMock(objects::DEVICE_HANDLER_MOCK, objects::NO_OBJECT);
DeviceHandlerMock deviceHandlerMock(objects::DEVICE_HANDLER_MOCK, objects::COM_IF_MOCK,
cookieIFMock, &deviceFdirMock);
ReturnValue_t result = deviceHandlerMock.initialize();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
DeviceHandlerCommander deviceHandlerCommander(objects::DEVICE_HANDLER_COMMANDER);
result = deviceHandlerCommander.initialize();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
SECTION("Commanding nominal") {
comIF.setTestCase(ComIFMock::TestCase::SIMPLE_COMMAND_NOMINAL);
result = deviceHandlerCommander.sendCommand(objects::DEVICE_HANDLER_MOCK,
DeviceHandlerMock::SIMPLE_COMMAND);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
deviceHandlerMock.performOperation(DeviceHandlerIF::PERFORM_OPERATION);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_READ);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_READ);
deviceHandlerCommander.performOperation();
result = deviceHandlerCommander.getReplyReturnCode();
uint32_t missedReplies = deviceFdirMock.getMissedReplyCount();
REQUIRE(missedReplies == 0);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
}
SECTION("Commanding missed reply") {
comIF.setTestCase(ComIFMock::TestCase::MISSED_REPLY);
deviceHandlerCommander.resetReplyReturnCode();
// Set the timeout to 0 to immediately timeout the reply
deviceHandlerMock.changeSimpleCommandReplyCountdown(0);
result = deviceHandlerCommander.sendCommand(objects::DEVICE_HANDLER_MOCK,
DeviceHandlerMock::SIMPLE_COMMAND);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
deviceHandlerMock.performOperation(DeviceHandlerIF::PERFORM_OPERATION);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_READ);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_READ);
deviceHandlerMock.performOperation(DeviceHandlerIF::PERFORM_OPERATION);
deviceHandlerCommander.performOperation();
result = deviceHandlerCommander.getReplyReturnCode();
REQUIRE(result == DeviceHandlerIF::TIMEOUT);
uint32_t missedReplies = deviceFdirMock.getMissedReplyCount();
REQUIRE(missedReplies == 1);
}
SECTION("Periodic reply nominal") {
comIF.setTestCase(ComIFMock::TestCase::PERIODIC_REPLY_NOMINAL);
deviceHandlerMock.enablePeriodicReply(DeviceHandlerMock::PERIODIC_REPLY);
deviceHandlerMock.performOperation(DeviceHandlerIF::PERFORM_OPERATION);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_READ);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_READ);
REQUIRE(deviceHandlerMock.getPeriodicReplyReceived() == true);
}
SECTION("Missed periodic reply") {
comIF.setTestCase(ComIFMock::TestCase::MISSED_REPLY);
// Set the timeout to 0 to immediately timeout the reply
deviceHandlerMock.changePeriodicReplyCountdown(0);
deviceHandlerMock.enablePeriodicReply(DeviceHandlerMock::PERIODIC_REPLY);
deviceHandlerMock.performOperation(DeviceHandlerIF::PERFORM_OPERATION);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_READ);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_READ);
uint32_t missedReplies = deviceFdirMock.getMissedReplyCount();
REQUIRE(missedReplies == 1);
// Test if disabling of periodic reply
deviceHandlerMock.disablePeriodicReply(DeviceHandlerMock::PERIODIC_REPLY);
deviceHandlerMock.performOperation(DeviceHandlerIF::PERFORM_OPERATION);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_WRITE);
deviceHandlerMock.performOperation(DeviceHandlerIF::SEND_READ);
deviceHandlerMock.performOperation(DeviceHandlerIF::GET_READ);
missedReplies = deviceFdirMock.getMissedReplyCount();
// Should still be 1 because periodic reply is now disabled
REQUIRE(missedReplies == 1);
}
}

View File

@ -0,0 +1,7 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
testDleEncoder.cpp
testOpDivider.cpp
testBitutil.cpp
testCRC.cpp
testTimevalOperations.cpp
)

View File

@ -0,0 +1,61 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/globalfunctions/bitutility.h"
TEST_CASE("Bitutility", "[Bitutility]") {
uint8_t dummyByte = 0;
bool bitSet = false;
for (uint8_t pos = 0; pos < 8; pos++) {
bitutil::set(&dummyByte, pos);
REQUIRE(dummyByte == (1 << (7 - pos)));
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == 1);
dummyByte = 0;
}
dummyByte = 0xff;
for (uint8_t pos = 0; pos < 8; pos++) {
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == 1);
bitutil::clear(&dummyByte, pos);
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == 0);
dummyByte = 0xff;
}
dummyByte = 0xf0;
for (uint8_t pos = 0; pos < 8; pos++) {
if (pos < 4) {
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == 1);
bitutil::toggle(&dummyByte, pos);
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == 0);
} else {
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == false);
bitutil::toggle(&dummyByte, pos);
bitutil::get(&dummyByte, pos, bitSet);
REQUIRE(bitSet == true);
}
}
REQUIRE(dummyByte == 0x0f);
dummyByte = 0;
bitutil::set(&dummyByte, 8);
REQUIRE(dummyByte == 0);
bitutil::set(&dummyByte, -1);
REQUIRE(dummyByte == 0);
dummyByte = 0xff;
bitutil::clear(&dummyByte, 8);
REQUIRE(dummyByte == 0xff);
bitutil::clear(&dummyByte, -1);
REQUIRE(dummyByte == 0xff);
dummyByte = 0x00;
bitutil::toggle(&dummyByte, 8);
REQUIRE(dummyByte == 0x00);
bitutil::toggle(&dummyByte, -1);
REQUIRE(dummyByte == 0x00);
REQUIRE(bitutil::get(&dummyByte, 8, bitSet) == false);
}

View File

@ -0,0 +1,14 @@
#include <array>
#include "catch2/catch_test_macros.hpp"
#include "fsfw/globalfunctions/CRC.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("CRC", "[CRC]") {
std::array<uint8_t, 10> testData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t crc = CRC::crc16ccitt(testData.data(), 10);
REQUIRE(crc == 49729);
for (uint8_t index = 0; index < testData.size(); index++) {
REQUIRE(testData[index] == index);
}
}

View File

@ -0,0 +1,212 @@
#include <array>
#include "catch2/catch_test_macros.hpp"
#include "fsfw/globalfunctions/DleEncoder.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
const std::vector<uint8_t> TEST_ARRAY_0 = {0, 0, 0, 0, 0};
const std::vector<uint8_t> TEST_ARRAY_1 = {0, DleEncoder::DLE_CHAR, 5};
const std::vector<uint8_t> TEST_ARRAY_2 = {0, DleEncoder::STX_CHAR, 5};
const std::vector<uint8_t> TEST_ARRAY_3 = {0, DleEncoder::CARRIAGE_RETURN, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_4 = {DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR,
DleEncoder::STX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_0_ENCODED_ESCAPED = {DleEncoder::STX_CHAR, 0, 0, 0, 0, 0,
DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_0_ENCODED_NON_ESCAPED = {
DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, 0, 0, 0, 0,
DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_1_ENCODED_ESCAPED = {
DleEncoder::STX_CHAR, 0, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR, 5, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_1_ENCODED_NON_ESCAPED = {
DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR, 5,
DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_2_ENCODED_ESCAPED = {
DleEncoder::STX_CHAR, 0, DleEncoder::DLE_CHAR,
DleEncoder::STX_CHAR + 0x40, 5, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_2_ENCODED_NON_ESCAPED = {
DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, DleEncoder::STX_CHAR, 5,
DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_3_ENCODED_ESCAPED = {
DleEncoder::STX_CHAR, 0,
DleEncoder::CARRIAGE_RETURN, DleEncoder::DLE_CHAR,
DleEncoder::ETX_CHAR + 0x40, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_3_ENCODED_NON_ESCAPED = {
DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0,
DleEncoder::CARRIAGE_RETURN, DleEncoder::ETX_CHAR, DleEncoder::DLE_CHAR,
DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_4_ENCODED_ESCAPED = {
DleEncoder::STX_CHAR, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR,
DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR + 0x40, DleEncoder::DLE_CHAR,
DleEncoder::STX_CHAR + 0x40, DleEncoder::ETX_CHAR};
const std::vector<uint8_t> TEST_ARRAY_4_ENCODED_NON_ESCAPED = {
DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR,
DleEncoder::ETX_CHAR, DleEncoder::STX_CHAR, DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR};
TEST_CASE("DleEncoder", "[DleEncoder]") {
DleEncoder dleEncoder;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 32> buffer;
size_t encodedLen = 0;
size_t readLen = 0;
size_t decodedLen = 0;
auto testLambdaEncode = [&](DleEncoder& encoder, const std::vector<uint8_t>& vecToEncode,
const std::vector<uint8_t>& expectedVec) {
result = encoder.encode(vecToEncode.data(), vecToEncode.size(), buffer.data(), buffer.size(),
&encodedLen);
REQUIRE(result == retval::CATCH_OK);
for (size_t idx = 0; idx < expectedVec.size(); idx++) {
REQUIRE(buffer[idx] == expectedVec[idx]);
}
REQUIRE(encodedLen == expectedVec.size());
};
auto testLambdaDecode = [&](DleEncoder& encoder, const std::vector<uint8_t>& testVecEncoded,
const std::vector<uint8_t>& expectedVec) {
result = encoder.decode(testVecEncoded.data(), testVecEncoded.size(), &readLen, buffer.data(),
buffer.size(), &decodedLen);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(readLen == testVecEncoded.size());
REQUIRE(decodedLen == expectedVec.size());
for (size_t idx = 0; idx < decodedLen; idx++) {
REQUIRE(buffer[idx] == expectedVec[idx]);
}
};
SECTION("Encoding") {
testLambdaEncode(dleEncoder, TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_ESCAPED);
auto testFaultyEncoding = [&](const std::vector<uint8_t>& vecToEncode,
const std::vector<uint8_t>& expectedVec) {
for (size_t faultyDestSize = 0; faultyDestSize < expectedVec.size(); faultyDestSize++) {
result = dleEncoder.encode(vecToEncode.data(), vecToEncode.size(), buffer.data(),
faultyDestSize, &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::STREAM_TOO_SHORT));
}
};
testFaultyEncoding(TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_ESCAPED);
testFaultyEncoding(TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_ESCAPED);
testFaultyEncoding(TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_ESCAPED);
testFaultyEncoding(TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_ESCAPED);
testFaultyEncoding(TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_ESCAPED);
dleEncoder.setEscapeMode(false);
testLambdaEncode(dleEncoder, TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_NON_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_NON_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_NON_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_NON_ESCAPED);
testLambdaEncode(dleEncoder, TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_NON_ESCAPED);
testFaultyEncoding(TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_NON_ESCAPED);
testFaultyEncoding(TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_NON_ESCAPED);
testFaultyEncoding(TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_NON_ESCAPED);
testFaultyEncoding(TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_NON_ESCAPED);
testFaultyEncoding(TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_NON_ESCAPED);
dleEncoder.setEscapeMode(true);
}
SECTION("Decoding") {
testLambdaDecode(dleEncoder, TEST_ARRAY_0_ENCODED_ESCAPED, TEST_ARRAY_0);
testLambdaDecode(dleEncoder, TEST_ARRAY_1_ENCODED_ESCAPED, TEST_ARRAY_1);
testLambdaDecode(dleEncoder, TEST_ARRAY_2_ENCODED_ESCAPED, TEST_ARRAY_2);
testLambdaDecode(dleEncoder, TEST_ARRAY_3_ENCODED_ESCAPED, TEST_ARRAY_3);
testLambdaDecode(dleEncoder, TEST_ARRAY_4_ENCODED_ESCAPED, TEST_ARRAY_4);
// Faulty source data
auto testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_ESCAPED;
testArray1EncodedFaulty[3] = 0;
result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
auto testArray2EncodedFaulty = TEST_ARRAY_2_ENCODED_ESCAPED;
testArray2EncodedFaulty[5] = 0;
result = dleEncoder.decode(testArray2EncodedFaulty.data(), testArray2EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
auto testArray4EncodedFaulty = TEST_ARRAY_4_ENCODED_ESCAPED;
testArray4EncodedFaulty[2] = 0;
result = dleEncoder.decode(testArray4EncodedFaulty.data(), testArray4EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
auto testArray4EncodedFaulty2 = TEST_ARRAY_4_ENCODED_ESCAPED;
testArray4EncodedFaulty2[4] = 0;
result = dleEncoder.decode(testArray4EncodedFaulty2.data(), testArray4EncodedFaulty2.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
auto testFaultyDecoding = [&](const std::vector<uint8_t>& vecToDecode,
const std::vector<uint8_t>& expectedVec) {
for (size_t faultyDestSizes = 0; faultyDestSizes < expectedVec.size(); faultyDestSizes++) {
result = dleEncoder.decode(vecToDecode.data(), vecToDecode.size(), &readLen, buffer.data(),
faultyDestSizes, &decodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::STREAM_TOO_SHORT));
}
};
testFaultyDecoding(TEST_ARRAY_0_ENCODED_ESCAPED, TEST_ARRAY_0);
testFaultyDecoding(TEST_ARRAY_1_ENCODED_ESCAPED, TEST_ARRAY_1);
testFaultyDecoding(TEST_ARRAY_2_ENCODED_ESCAPED, TEST_ARRAY_2);
testFaultyDecoding(TEST_ARRAY_3_ENCODED_ESCAPED, TEST_ARRAY_3);
testFaultyDecoding(TEST_ARRAY_4_ENCODED_ESCAPED, TEST_ARRAY_4);
dleEncoder.setEscapeMode(false);
testLambdaDecode(dleEncoder, TEST_ARRAY_0_ENCODED_NON_ESCAPED, TEST_ARRAY_0);
testLambdaDecode(dleEncoder, TEST_ARRAY_1_ENCODED_NON_ESCAPED, TEST_ARRAY_1);
testLambdaDecode(dleEncoder, TEST_ARRAY_2_ENCODED_NON_ESCAPED, TEST_ARRAY_2);
testLambdaDecode(dleEncoder, TEST_ARRAY_3_ENCODED_NON_ESCAPED, TEST_ARRAY_3);
testLambdaDecode(dleEncoder, TEST_ARRAY_4_ENCODED_NON_ESCAPED, TEST_ARRAY_4);
testFaultyDecoding(TEST_ARRAY_0_ENCODED_NON_ESCAPED, TEST_ARRAY_0);
testFaultyDecoding(TEST_ARRAY_1_ENCODED_NON_ESCAPED, TEST_ARRAY_1);
testFaultyDecoding(TEST_ARRAY_2_ENCODED_NON_ESCAPED, TEST_ARRAY_2);
testFaultyDecoding(TEST_ARRAY_3_ENCODED_NON_ESCAPED, TEST_ARRAY_3);
testFaultyDecoding(TEST_ARRAY_4_ENCODED_NON_ESCAPED, TEST_ARRAY_4);
// Faulty source data
testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_NON_ESCAPED;
auto prevVal = testArray1EncodedFaulty[0];
testArray1EncodedFaulty[0] = 0;
result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
testArray1EncodedFaulty[0] = prevVal;
testArray1EncodedFaulty[1] = 0;
result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_NON_ESCAPED;
testArray1EncodedFaulty[6] = 0;
result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_NON_ESCAPED;
testArray1EncodedFaulty[7] = 0;
result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
testArray4EncodedFaulty = TEST_ARRAY_4_ENCODED_NON_ESCAPED;
testArray4EncodedFaulty[3] = 0;
result = dleEncoder.decode(testArray4EncodedFaulty.data(), testArray4EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
dleEncoder.setEscapeMode(true);
testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_ESCAPED;
testArray1EncodedFaulty[5] = 0;
result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(),
&readLen, buffer.data(), buffer.size(), &encodedLen);
REQUIRE(result == static_cast<int>(DleEncoder::DECODING_ERROR));
}
}

View File

@ -0,0 +1,65 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
TEST_CASE("OpDivider", "[OpDivider]") {
auto opDivider = PeriodicOperationDivider(1);
REQUIRE(opDivider.getDivider() == 1);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.check() == true);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.check() == true);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.checkAndIncrement() == true);
opDivider.setDivider(0);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.checkAndIncrement() == true);
opDivider.setDivider(2);
opDivider.resetCounter();
REQUIRE(opDivider.getDivider() == 2);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.check() == false);
REQUIRE(opDivider.checkAndIncrement() == false);
REQUIRE(opDivider.getCounter() == 2);
REQUIRE(opDivider.check() == true);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.check() == false);
REQUIRE(opDivider.checkAndIncrement() == false);
REQUIRE(opDivider.getCounter() == 2);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.checkAndIncrement() == false);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.checkAndIncrement() == false);
opDivider.setDivider(3);
opDivider.resetCounter();
REQUIRE(opDivider.checkAndIncrement() == false);
REQUIRE(opDivider.checkAndIncrement() == false);
REQUIRE(opDivider.getCounter() == 3);
REQUIRE(opDivider.checkAndIncrement() == true);
REQUIRE(opDivider.getCounter() == 1);
REQUIRE(opDivider.checkAndIncrement() == false);
auto opDividerNonResetting = PeriodicOperationDivider(2, false);
REQUIRE(opDividerNonResetting.getCounter() == 1);
REQUIRE(opDividerNonResetting.check() == false);
REQUIRE(opDividerNonResetting.checkAndIncrement() == false);
REQUIRE(opDividerNonResetting.getCounter() == 2);
REQUIRE(opDividerNonResetting.check() == true);
REQUIRE(opDividerNonResetting.checkAndIncrement() == true);
REQUIRE(opDividerNonResetting.getCounter() == 3);
REQUIRE(opDividerNonResetting.checkAndIncrement() == true);
REQUIRE(opDividerNonResetting.getCounter() == 4);
opDividerNonResetting.resetCounter();
REQUIRE(opDividerNonResetting.getCounter() == 1);
REQUIRE(opDividerNonResetting.check() == false);
REQUIRE(opDividerNonResetting.checkAndIncrement() == false);
REQUIRE(opDividerNonResetting.getCounter() == 2);
}

View File

@ -0,0 +1,124 @@
#include <fsfw/globalfunctions/timevalOperations.h>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("TimevalTest", "[timevalOperations]") {
SECTION("Comparison") {
timeval t1;
t1.tv_sec = 1648227422;
t1.tv_usec = 123456;
timeval t2;
t2.tv_sec = 1648227422;
t2.tv_usec = 123456;
REQUIRE(t1 == t2);
REQUIRE(t2 == t1);
REQUIRE_FALSE(t1 != t2);
REQUIRE_FALSE(t2 != t1);
REQUIRE(t1 <= t2);
REQUIRE(t2 <= t1);
REQUIRE(t1 >= t2);
REQUIRE(t2 >= t1);
REQUIRE_FALSE(t1 < t2);
REQUIRE_FALSE(t2 < t1);
REQUIRE_FALSE(t1 > t2);
REQUIRE_FALSE(t2 > t1);
timeval t3;
t3.tv_sec = 1648227422;
t3.tv_usec = 123457;
REQUIRE_FALSE(t1 == t3);
REQUIRE(t1 != t3);
REQUIRE(t1 <= t3);
REQUIRE_FALSE(t3 <= t1);
REQUIRE_FALSE(t1 >= t3);
REQUIRE(t3 >= t1);
REQUIRE(t1 < t3);
REQUIRE_FALSE(t3 < t1);
REQUIRE_FALSE(t1 > t3);
REQUIRE(t3 > t1);
timeval t4;
t4.tv_sec = 1648227423;
t4.tv_usec = 123456;
REQUIRE_FALSE(t1 == t4);
REQUIRE(t1 != t4);
REQUIRE(t1 <= t4);
REQUIRE_FALSE(t4 <= t1);
REQUIRE_FALSE(t1 >= t4);
REQUIRE(t4 >= t1);
REQUIRE(t1 < t4);
REQUIRE_FALSE(t4 < t1);
REQUIRE_FALSE(t1 > t4);
REQUIRE(t4 > t1);
}
SECTION("Operators") {
timeval t1;
t1.tv_sec = 1648227422;
t1.tv_usec = 123456;
timeval t2;
t2.tv_sec = 1648227422;
t2.tv_usec = 123456;
timeval t3 = t1 - t2;
REQUIRE(t3.tv_sec == 0);
REQUIRE(t3.tv_usec == 0);
timeval t4 = t1 - t3;
REQUIRE(t4.tv_sec == 1648227422);
REQUIRE(t4.tv_usec == 123456);
timeval t5 = t3 - t1;
REQUIRE(t5.tv_sec == -1648227422);
REQUIRE(t5.tv_usec == -123456);
timeval t6;
t6.tv_sec = 1648227400;
t6.tv_usec = 999999;
timeval t7 = t6 + t1;
REQUIRE(t7.tv_sec == (1648227422ull + 1648227400ull + 1ull));
REQUIRE(t7.tv_usec == 123455);
timeval t8 = t1 - t6;
REQUIRE(t8.tv_sec == 1648227422 - 1648227400 - 1);
REQUIRE(t8.tv_usec == 123457);
double scalar = 2;
timeval t9 = t1 * scalar;
REQUIRE(t9.tv_sec == 3296454844);
REQUIRE(t9.tv_usec == 246912);
timeval t10 = scalar * t1;
REQUIRE(t10.tv_sec == 3296454844);
REQUIRE(t10.tv_usec == 246912);
timeval t11 = t6 * scalar;
REQUIRE(t11.tv_sec == (3296454800 + 1));
REQUIRE(t11.tv_usec == 999998);
timeval t12 = t1 / scalar;
REQUIRE(t12.tv_sec == 824113711);
REQUIRE(t12.tv_usec == 61728);
timeval t13 = t6 / scalar;
REQUIRE(t13.tv_sec == 824113700);
// Rounding issue
REQUIRE(t13.tv_usec == 499999);
double scalar2 = t9 / t1;
REQUIRE(scalar2 == Catch::Approx(2.0));
double scalar3 = t1 / t6;
REQUIRE(scalar3 == Catch::Approx(1.000000013));
double scalar4 = t3 / t1;
REQUIRE(scalar4 == Catch::Approx(0));
double scalar5 = t12 / t1;
REQUIRE(scalar5 == Catch::Approx(0.5));
}
SECTION("timevalOperations::toTimeval") {
double seconds = 1648227422.123456;
timeval t1 = timevalOperations::toTimeval(seconds);
REQUIRE(t1.tv_sec == 1648227422);
// Allow 1 usec rounding tolerance
REQUIRE(t1.tv_usec >= 123455);
REQUIRE(t1.tv_usec <= 123457);
}
}

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
testCommandExecutor.cpp
)

View File

@ -0,0 +1,129 @@
#include <unistd.h>
#include <catch2/catch_test_macros.hpp>
#include <fstream>
#include <iostream>
#include "fsfw/container/DynamicFIFO.h"
#include "fsfw/container/SimpleRingBuffer.h"
#include "fsfw/platform.h"
#include "fsfw/serviceinterface.h"
#include "fsfw_hal/linux/CommandExecutor.h"
#include "tests/TestsConfig.h"
#ifdef PLATFORM_UNIX
static const char TEST_FILE_NAME[] = "/tmp/fsfw-unittest-test.txt";
TEST_CASE("Command Executor", "[cmd-exec]") {
// Check blocking mode first
CommandExecutor cmdExecutor(1024);
std::string cmd = "echo \"test\" >> " + std::string(TEST_FILE_NAME);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::IDLE);
ReturnValue_t result = cmdExecutor.load(cmd, true, true);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::COMMAND_LOADED);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(cmdExecutor.execute() == HasReturnvaluesIF::RETURN_OK);
// Check that file exists with contents
std::ifstream file(TEST_FILE_NAME);
std::string line;
std::getline(file, line);
CHECK(line == "test");
std::remove(TEST_FILE_NAME);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::IDLE);
// Now check non-blocking mode
SimpleRingBuffer outputBuffer(524, true);
DynamicFIFO<uint16_t> sizesFifo(12);
cmdExecutor.setRingBuffer(&outputBuffer, &sizesFifo);
result = cmdExecutor.load("echo \"Hello World\"", false, false);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
cmdExecutor.execute();
bool bytesHaveBeenRead = false;
size_t limitIdx = 0;
while (result != CommandExecutor::EXECUTION_FINISHED) {
limitIdx++;
result = cmdExecutor.check(bytesHaveBeenRead);
REQUIRE(result != CommandExecutor::COMMAND_ERROR);
usleep(500);
REQUIRE(limitIdx < 500);
}
limitIdx = 0;
CHECK(bytesHaveBeenRead == true);
CHECK(result == CommandExecutor::EXECUTION_FINISHED);
uint16_t readBytes = 0;
sizesFifo.retrieve(&readBytes);
REQUIRE(readBytes == 12);
REQUIRE(outputBuffer.getAvailableReadData() == 12);
uint8_t readBuffer[32] = {};
REQUIRE(outputBuffer.readData(readBuffer, 12) == HasReturnvaluesIF::RETURN_OK);
std::string readString(reinterpret_cast<char*>(readBuffer));
std::string cmpString = "Hello World\n";
CHECK(readString == cmpString);
outputBuffer.deleteData(12, true);
// Issues with CI/CD
#if FSFW_CICD_BUILD == 0
// Test more complex command
result = cmdExecutor.load("ping -c 1 localhost", false, false);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::COMMAND_LOADED);
REQUIRE(cmdExecutor.execute() == HasReturnvaluesIF::RETURN_OK);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING);
limitIdx = 0;
while (result != CommandExecutor::EXECUTION_FINISHED) {
limitIdx++;
result = cmdExecutor.check(bytesHaveBeenRead);
REQUIRE(result != CommandExecutor::COMMAND_ERROR);
// This ensures that the tests do not block indefinitely
usleep(500);
REQUIRE(limitIdx < 500);
}
limitIdx = 0;
CHECK(bytesHaveBeenRead == true);
CHECK(result == CommandExecutor::EXECUTION_FINISHED);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::IDLE);
readBytes = 0;
sizesFifo.retrieve(&readBytes);
uint8_t largerReadBuffer[1024] = {};
// That's about the size of the reply
bool beTrue = (readBytes > 100) and (readBytes < 400);
if (not beTrue) {
size_t readLen = outputBuffer.getAvailableReadData();
if (readLen > sizeof(largerReadBuffer) - 1) {
readLen = sizeof(largerReadBuffer) - 1;
}
outputBuffer.readData(largerReadBuffer, readLen);
std::string readString(reinterpret_cast<char*>(largerReadBuffer));
std::cerr << "Catch2 tag cmd-exec: Read " << readBytes << ": " << std::endl;
std::cerr << readString << std::endl;
}
REQUIRE(beTrue);
outputBuffer.readData(largerReadBuffer, readBytes);
// You can also check this output in the debugger
std::string allTheReply(reinterpret_cast<char*>(largerReadBuffer));
// I am just going to assume that this string is the same across ping implementations
// of different Linux systems
REQUIRE(allTheReply.find("PING localhost") != std::string::npos);
#endif
// Now check failing command
result = cmdExecutor.load("false", false, false);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
result = cmdExecutor.execute();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
while (result != CommandExecutor::EXECUTION_FINISHED and
result != HasReturnvaluesIF::RETURN_FAILED) {
limitIdx++;
result = cmdExecutor.check(bytesHaveBeenRead);
REQUIRE(result != CommandExecutor::COMMAND_ERROR);
// This ensures that the tests do not block indefinitely
usleep(500);
REQUIRE(limitIdx < 500);
}
REQUIRE(result == HasReturnvaluesIF::RETURN_FAILED);
REQUIRE(cmdExecutor.getLastError() == 1);
}
#endif

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
TestInternalErrorReporter.cpp
)

View File

@ -0,0 +1,120 @@
#include <fsfw/housekeeping/HousekeepingSnapshot.h>
#include <fsfw/internalerror/InternalErrorReporter.h>
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/timemanager/CCSDSTime.h>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/action/ActionMessage.h"
#include "fsfw/ipc/CommandMessage.h"
#include "fsfw/ipc/MessageQueueMessage.h"
#include "fsfw/objectmanager/frameworkObjects.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
#include "fsfw_tests/unit/mocks/PeriodicTaskIFMock.h"
TEST_CASE("Internal Error Reporter", "[TestInternalError]") {
PeriodicTaskMock task(10, nullptr);
ObjectManagerIF* manager = ObjectManager::instance();
if (manager == nullptr) {
FAIL();
}
InternalErrorReporter* internalErrorReporter = dynamic_cast<InternalErrorReporter*>(
ObjectManager::instance()->get<InternalErrorReporterIF>(objects::INTERNAL_ERROR_REPORTER));
if (internalErrorReporter == nullptr) {
FAIL();
}
task.addComponent(objects::INTERNAL_ERROR_REPORTER);
// This calls the initializeAfterTaskCreation function
task.startTask();
MessageQueueIF* testQueue = QueueFactory::instance()->createMessageQueue(1);
MessageQueueIF* hkQueue = QueueFactory::instance()->createMessageQueue(1);
internalErrorReporter->getSubscriptionInterface()->subscribeForSetUpdateMessage(
InternalErrorDataset::ERROR_SET_ID, objects::NO_OBJECT, hkQueue->getId(), true);
StorageManagerIF* ipcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
SECTION("MessageQueueFull") {
CommandMessage message;
ActionMessage::setCompletionReply(&message, 10, true);
auto result = hkQueue->sendMessage(testQueue->getId(), &message);
REQUIRE(result == retval::CATCH_OK);
uint32_t queueHits = 0;
uint32_t lostTm = 0;
uint32_t storeHits = 0;
/* We don't know if another test caused a queue Hit so we will enforce one,
then remeber the queueHit count and force another hit */
internalErrorReporter->queueMessageNotSent();
internalErrorReporter->performOperation(0);
{
CommandMessage hkMessage;
result = hkQueue->receiveMessage(&hkMessage);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(hkMessage.getCommand() == HousekeepingMessage::UPDATE_SNAPSHOT_SET);
store_address_t storeAddress;
gp_id_t gpid =
HousekeepingMessage::getUpdateSnapshotVariableCommand(&hkMessage, &storeAddress);
REQUIRE(gpid.objectId == objects::INTERNAL_ERROR_REPORTER);
// We need the object ID of the reporter here (NO_OBJECT)
InternalErrorDataset dataset(objects::INTERNAL_ERROR_REPORTER);
CCSDSTime::CDS_short time;
ConstAccessorPair data = ipcStore->getData(storeAddress);
REQUIRE(data.first == HasReturnvaluesIF::RETURN_OK);
HousekeepingSnapshot hkSnapshot(&time, &dataset);
const uint8_t* buffer = data.second.data();
size_t size = data.second.size();
result = hkSnapshot.deSerialize(&buffer, &size, SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Remember the amount of queueHits before to see the increase
queueHits = dataset.queueHits.value;
lostTm = dataset.tmHits.value;
storeHits = dataset.storeHits.value;
}
result = hkQueue->sendMessage(testQueue->getId(), &message);
REQUIRE(result == MessageQueueIF::FULL);
internalErrorReporter->lostTm();
internalErrorReporter->storeFull();
{
internalErrorReporter->performOperation(0);
CommandMessage hkMessage;
result = hkQueue->receiveMessage(&hkMessage);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(hkMessage.getCommand() == HousekeepingMessage::UPDATE_SNAPSHOT_SET);
store_address_t storeAddress;
gp_id_t gpid =
HousekeepingMessage::getUpdateSnapshotVariableCommand(&hkMessage, &storeAddress);
REQUIRE(gpid.objectId == objects::INTERNAL_ERROR_REPORTER);
ConstAccessorPair data = ipcStore->getData(storeAddress);
REQUIRE(data.first == HasReturnvaluesIF::RETURN_OK);
CCSDSTime::CDS_short time;
// We need the object ID of the reporter here (NO_OBJECT)
InternalErrorDataset dataset(objects::INTERNAL_ERROR_REPORTER);
HousekeepingSnapshot hkSnapshot(&time, &dataset);
const uint8_t* buffer = data.second.data();
size_t size = data.second.size();
result = hkSnapshot.deSerialize(&buffer, &size, SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Test that we had one more queueHit
REQUIRE(dataset.queueHits.value == (queueHits + 1));
REQUIRE(dataset.tmHits.value == (lostTm + 1));
REQUIRE(dataset.storeHits.value == (storeHits + 1));
}
// Complete Coverage
internalErrorReporter->setDiagnosticPrintout(true);
internalErrorReporter->setMutexTimeout(MutexIF::TimeoutType::BLOCKING, 0);
{
// Message Queue Id
MessageQueueId_t id = internalErrorReporter->getCommandQueue();
REQUIRE(id != MessageQueueIF::NO_QUEUE);
CommandMessage message;
sid_t sid(objects::INTERNAL_ERROR_REPORTER, InternalErrorDataset::ERROR_SET_ID);
HousekeepingMessage::setToggleReportingCommand(&message, sid, true, false);
result = hkQueue->sendMessage(id, &message);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
internalErrorReporter->performOperation(0);
}
}
QueueFactory::instance()->deleteMessageQueue(testQueue);
QueueFactory::instance()->deleteMessageQueue(hkQueue);
}

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
PowerSwitcherMock.cpp
)

View File

@ -0,0 +1,14 @@
#ifndef FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_
#define FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_
#include <fsfw/housekeeping/AcceptsHkPacketsIF.h>
#include <fsfw/objectmanager/SystemObject.h>
class HkReceiverMock : public SystemObject, public AcceptsHkPacketsIF {
public:
HkReceiverMock(object_id_t objectId) : SystemObject(objectId) {}
MessageQueueId_t getHkQueue() const { return MessageQueueIF::NO_QUEUE; }
};
#endif /* FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_ */

Some files were not shown because too many files have changed in this diff Show More