diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 361f7dc34..0d3baa887 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -3,8 +3,9 @@ #include "../ipc/MessageQueueSenderIF.h" #include "../objectmanager/ObjectManagerIF.h" -ActionHelper::ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - owner(setOwner), queueToUse(useThisQueue), ipcStore(nullptr) { +ActionHelper::ActionHelper(HasActionsIF* setOwner, + MessageQueueIF* useThisQueue) : + owner(setOwner), queueToUse(useThisQueue) { } ActionHelper::~ActionHelper() { @@ -33,13 +34,15 @@ ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) { return HasReturnvaluesIF::RETURN_OK; } -void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { +void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, ReturnValue_t result) { CommandMessage reply; ActionMessage::setStepReply(&reply, commandId, step + STEP_OFFSET, result); queueToUse->sendMessage(reportTo, &reply); } -void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { +void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result) { CommandMessage reply; ActionMessage::setCompletionReply(&reply, commandId, result); queueToUse->sendMessage(reportTo, &reply); @@ -49,8 +52,8 @@ void ActionHelper::setQueueToUse(MessageQueueIF* queue) { queueToUse = queue; } -void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, - store_address_t dataAddress) { +void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress) { const uint8_t* dataPtr = NULL; size_t size = 0; ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); @@ -62,6 +65,11 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t act } result = owner->executeAction(actionId, commandedBy, dataPtr, size); ipcStore->deleteData(dataAddress); + if(result == HasActionsIF::EXECUTION_FINISHED) { + CommandMessage reply; + ActionMessage::setCompletionReply(&reply, actionId, result); + queueToUse->sendMessage(commandedBy, &reply); + } if (result != HasReturnvaluesIF::RETURN_OK) { CommandMessage reply; ActionMessage::setStepReply(&reply, actionId, 0, result); @@ -86,22 +94,28 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, if (result != HasReturnvaluesIF::RETURN_OK) { return result; } - result = data->serialize(&dataPtr, &size, maxSize, SerializeIF::Endianness::BIG); + result = data->serialize(&dataPtr, &size, maxSize, + SerializeIF::Endianness::BIG); if (result != HasReturnvaluesIF::RETURN_OK) { ipcStore->deleteData(storeAddress); return result; } - //We don't need to report the objectId, as we receive REQUESTED data before the completion success message. - //True aperiodic replies need to be reported with another dedicated message. + // We don't need to report the objectId, as we receive REQUESTED data + // before the completion success message. + // True aperiodic replies need to be reported with + // another dedicated message. ActionMessage::setDataReply(&reply, replyId, storeAddress); - //TODO Service Implementation sucks at the moment - if (hideSender){ + // If the sender needs to be hidden, for example to handle packet + // as unrequested reply, this will be done here. + if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); - } else { + } + else { result = queueToUse->sendMessage(reportTo, &reply); } - if ( result != HasReturnvaluesIF::RETURN_OK){ + + if (result != HasReturnvaluesIF::RETURN_OK){ ipcStore->deleteData(storeAddress); } return result; @@ -109,3 +123,39 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, void ActionHelper::resetHelper() { } + +ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, + ActionId_t replyId, const uint8_t *data, size_t dataSize, + bool hideSender) { + CommandMessage reply; + store_address_t storeAddress; + ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + if (result != HasReturnvaluesIF::RETURN_OK) { + ipcStore->deleteData(storeAddress); + return result; + } + + // We don't need to report the objectId, as we receive REQUESTED data + // before the completion success message. + // True aperiodic replies need to be reported with + // another dedicated message. + ActionMessage::setDataReply(&reply, replyId, storeAddress); + + // If the sender needs to be hidden, for example to handle packet + // as unrequested reply, this will be done here. + if (hideSender) { + result = MessageQueueSenderIF::sendMessage(reportTo, &reply); + } + else { + result = queueToUse->sendMessage(reportTo, &reply); + } + + if (result != HasReturnvaluesIF::RETURN_OK){ + ipcStore->deleteData(storeAddress); + } + return result; +} diff --git a/action/ActionHelper.h b/action/ActionHelper.h index bbc6d114d..a91722f39 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -1,15 +1,18 @@ -#ifndef ACTIONHELPER_H_ -#define ACTIONHELPER_H_ +#ifndef FSFW_ACTION_ACTIONHELPER_H_ +#define FSFW_ACTION_ACTIONHELPER_H_ #include "ActionMessage.h" #include "../serialize/SerializeIF.h" #include "../ipc/MessageQueueIF.h" /** - * \brief Action Helper is a helper class which handles action messages + * @brief Action Helper is a helper class which handles action messages * - * Components which use the HasActionIF this helper can be used to handle the action messages. - * It does handle step messages as well as other answers to action calls. It uses the executeAction function - * of its owner as callback. The call of the initialize function is mandatory and it needs a valid messageQueueIF pointer! + * Components which use the HasActionIF this helper can be used to handle + * the action messages. + * It does handle step messages as well as other answers to action calls. + * It uses the executeAction function of its owner as callback. + * The call of the initialize function is mandatory and needs a + * valid MessageQueueIF pointer! */ class HasActionsIF; @@ -18,7 +21,8 @@ public: /** * Constructor of the action helper * @param setOwner Pointer to the owner of the interface - * @param useThisQueue messageQueue to be used, can be set during initialize function as well. + * @param useThisQueue messageQueue to be used, can be set during + * initialize function as well. */ ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); @@ -26,28 +30,36 @@ public: /** * Function to be called from the owner with a new command message * - * If the message is a valid action message the helper will use the executeAction function from HasActionsIF. - * If the message is invalid or the callback fails a message reply will be send to the sender of the message automatically. + * If the message is a valid action message the helper will use the + * executeAction function from HasActionsIF. + * If the message is invalid or the callback fails a message reply will be + * send to the sender of the message automatically. * * @param command Pointer to a command message received by the owner - * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, CommandMessage::UNKNOW_COMMAND if this message ID is unkown + * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, + * CommandMessage::UNKNOW_COMMAND if this message ID is unkown */ ReturnValue_t handleActionMessage(CommandMessage* command); /** - * Helper initialize function. Must be called before use of any other helper function - * @param queueToUse_ Pointer to the messageQueue to be used, optional if queue was set in constructor + * Helper initialize function. Must be called before use of any other + * helper function + * @param queueToUse_ Pointer to the messageQueue to be used, optional + * if queue was set in constructor * @return Returns RETURN_OK if successful */ ReturnValue_t initialize(MessageQueueIF* queueToUse_ = nullptr); /** - * Function to be called from the owner to send a step message. Success or failure will be determined by the result value. + * Function to be called from the owner to send a step message. + * Success or failure will be determined by the result value. * * @param step Number of steps already done * @param reportTo The messageQueueId to report the step message to * @param commandId ID of the executed command * @param result Result of the execution */ - void step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** * Function to be called by the owner to send a action completion message * @@ -55,39 +67,59 @@ public: * @param commandId ID of the executed command * @param result Result of the execution */ - void finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void finish(MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** - * Function to be called by the owner if an action does report data - * - * @param reportTo MessageQueueId_t to report the action completion message to + * Function to be called by the owner if an action does report data. + * Takes a SerializeIF* pointer and serializes it into the IPC store. + * @param reportTo MessageQueueId_t to report the action completion + * message to * @param replyId ID of the executed command * @param data Pointer to the data * @return Returns RETURN_OK if successful, otherwise failure code */ - ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, SerializeIF* data, bool hideSender = false); + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + SerializeIF* data, bool hideSender = false); + /** + * Function to be called by the owner if an action does report data. + * Takes the raw data and writes it into the IPC store. + * @param reportTo MessageQueueId_t to report the action completion + * message to + * @param replyId ID of the executed command + * @param data Pointer to the data + * @return Returns RETURN_OK if successful, otherwise failure code + */ + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + const uint8_t* data, size_t dataSize, bool hideSender = false); /** - * Function to setup the MessageQueueIF* of the helper. Can be used to set the messageQueueIF* if - * message queue is unavailable at construction and initialize but must be setup before first call of other functions. + * Function to setup the MessageQueueIF* of the helper. Can be used to + * set the MessageQueueIF* if message queue is unavailable at construction + * and initialize but must be setup before first call of other functions. * @param queue Queue to be used by the helper */ void setQueueToUse(MessageQueueIF *queue); protected: - static const uint8_t STEP_OFFSET = 1;//!< Increase of value of this per step + //!< Increase of value of this per step + static const uint8_t STEP_OFFSET = 1; HasActionsIF* owner;//!< Pointer to the owner - MessageQueueIF* queueToUse;//!< Queue to be used as response sender, has to be set with - StorageManagerIF* ipcStore;//!< Pointer to an IPC Store, initialized during construction or initialize(MessageQueueIF* queueToUse_) or with setQueueToUse(MessageQueueIF *queue) + //! Queue to be used as response sender, has to be set in ctor or with + //! setQueueToUse + MessageQueueIF* queueToUse; + //! Pointer to an IPC Store, initialized during construction or + StorageManagerIF* ipcStore = nullptr; + /** - *Internal function called by handleActionMessage(CommandMessage* command) - * + * Internal function called by handleActionMessage * @param commandedBy MessageQueueID of Commander * @param actionId ID of action to be done * @param dataAddress Address of additional data in IPC Store */ - virtual void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress); + virtual void prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress); /** - * + * @brief Default implementation is empty. */ virtual void resetHelper(); }; -#endif /* ACTIONHELPER_H_ */ +#endif /* FSFW_ACTION_ACTIONHELPER_H_ */ diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index 886d08377..690f03698 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -1,11 +1,12 @@ -#ifndef FRAMEWORK_ACTION_HASACTIONSIF_H_ -#define FRAMEWORK_ACTION_HASACTIONSIF_H_ +#ifndef FSFW_ACTION_HASACTIONSIF_H_ +#define FSFW_ACTION_HASACTIONSIF_H_ #include "ActionHelper.h" #include "ActionMessage.h" #include "SimpleActionHelper.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../ipc/MessageQueueIF.h" + /** * @brief * Interface for component which uses actions @@ -47,14 +48,16 @@ public: virtual MessageQueueId_t getCommandQueue() const = 0; /** * Execute or initialize the execution of a certain function. - * Returning #EXECUTION_FINISHED or a failure code, nothing else needs to - * be done. When needing more steps, return RETURN_OK and issue steps and - * completion manually. - * One "step failed" or completion report must be issued! + * The ActionHelpers will execute this function and behave differently + * depending on the returnvalue. + * + * @return + * -@c EXECUTION_FINISHED Finish reply will be generated + * -@c Not RETURN_OK Step failure reply will be generated */ virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size) = 0; }; -#endif /* FRAMEWORK_ACTION_HASACTIONSIF_H_ */ +#endif /* FSFW_ACTION_HASACTIONSIF_H_ */ diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index d79a3c977..af57736c8 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -1,16 +1,17 @@ #include "HasActionsIF.h" #include "SimpleActionHelper.h" + SimpleActionHelper::SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - ActionHelper(setOwner, useThisQueue), isExecuting(false), lastCommander( - 0), lastAction(0), stepCount(0) { + ActionHelper(setOwner, useThisQueue), isExecuting(false) { } SimpleActionHelper::~SimpleActionHelper() { } void SimpleActionHelper::step(ReturnValue_t result) { - //STEP_OFFESET is subtracted to compensate for adding offset in base method, which is not necessary here. + // STEP_OFFESET is subtracted to compensate for adding offset in base + // method, which is not necessary here. ActionHelper::step(stepCount - STEP_OFFSET, lastCommander, lastAction, result); if (result != HasReturnvaluesIF::RETURN_OK) { diff --git a/action/SimpleActionHelper.h b/action/SimpleActionHelper.h index 1329b5fb5..1f35d9fdf 100644 --- a/action/SimpleActionHelper.h +++ b/action/SimpleActionHelper.h @@ -1,8 +1,13 @@ -#ifndef SIMPLEACTIONHELPER_H_ -#define SIMPLEACTIONHELPER_H_ +#ifndef FSFW_ACTION_SIMPLEACTIONHELPER_H_ +#define FSFW_ACTION_SIMPLEACTIONHELPER_H_ #include "ActionHelper.h" +/** + * @brief This is an action helper which is only able to service one action + * at a time but remembers last commander and last action which + * simplifies usage + */ class SimpleActionHelper: public ActionHelper { public: SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); @@ -12,13 +17,14 @@ public: ReturnValue_t reportData(SerializeIF* data); protected: - void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress); - virtual void resetHelper(); + void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, + store_address_t dataAddress); + virtual void resetHelper(); private: bool isExecuting; - MessageQueueId_t lastCommander; - ActionId_t lastAction; - uint8_t stepCount; + MessageQueueId_t lastCommander = MessageQueueIF::NO_QUEUE; + ActionId_t lastAction = 0; + uint8_t stepCount = 0; }; #endif /* SIMPLEACTIONHELPER_H_ */ diff --git a/events/Event.h b/events/Event.h index e22c9db16..f8410f328 100644 --- a/events/Event.h +++ b/events/Event.h @@ -4,7 +4,7 @@ #include #include "fwSubsystemIdRanges.h" //could be move to more suitable location -#include +#include typedef uint16_t EventId_t; typedef uint8_t EventSeverity_t; diff --git a/unittest/README.md b/unittest/README.md new file mode 100644 index 000000000..e628d43e8 --- /dev/null +++ b/unittest/README.md @@ -0,0 +1,49 @@ +## FSFW Testing +This repository contains testing and unit testing components. + +[Catch2](https://github.com/catchorg/Catch2) has been used as a framework, +and these unit tests can only be run on a linux host machine. +The makefile with default settings creates the unit test binary which can be +run in the terminal or in eclipse. + +### Instructions + +### Eclipse CDT settings + +The default eclipse terminal has issues displaying the colors used +when running the unit test binary by catch2. To fix this issue, +install the ANSI Escape In Console package from the eclipse marketplace. + +### GCOV integration + +GCOV has been integrated as a code coverage tool. +It can be enabled by adding `GCOV=1` to the build process as an additional argument. +Coverage data will be provided in form of .gcno and .gcda files. +These can be displayed in eclipse by looking +for a .gcno or .gcda file in the \_obj folder, double-clicking it +and picking the right source-binary. This will generate +information about which lines of a file have run, provided it is open in +eclipse. + +### LCOV integration + +The files generated by GCOV can also be processed by the tool LCOV. +On ubuntu, the tool can be installed with the following command: + +```sh +sudo apt-get install lcov +```` + +After that, the tool can be run by building the unit tests with `GCOV=1`, +running them at least one time and then executing the `lcov.sh` script. + +### Adding unit tests + +The catch unit tests are located in unittest/testfw. To add new unit tests, +add them to the UnitTestCatch.cpp file or add a new source file which +includes catch.hpp. + +For writing basics tests, the [assertion documentation](https://github.com/catchorg/Catch2/blob/master/docs/assertions.md#top) +or the existing examples are a good guideliens. +For more advanced tests, refer to the [catch2 documentation](https://github.com/catchorg/Catch2/blob/master/docs/Readme.md#top). + diff --git a/unittest/core/CatchDefinitions.cpp b/unittest/core/CatchDefinitions.cpp new file mode 100644 index 000000000..0b66558ac --- /dev/null +++ b/unittest/core/CatchDefinitions.cpp @@ -0,0 +1,11 @@ +#include "CatchDefinitions.h" +#include + +StorageManagerIF* tglob::getIpcStoreHandle() { + if(objectManager != nullptr) { + return objectManager->get(objects::IPC_STORE); + } else { + sif::error << "Global object manager uninitialized" << std::endl; + return nullptr; + } +} diff --git a/unittest/core/CatchDefinitions.h b/unittest/core/CatchDefinitions.h new file mode 100644 index 000000000..366cda7e5 --- /dev/null +++ b/unittest/core/CatchDefinitions.h @@ -0,0 +1,21 @@ +#ifndef FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ +#define FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ + +#include +#include +#include + +namespace retval { +static constexpr int CATCH_OK = static_cast(HasReturnvaluesIF::RETURN_OK); +static constexpr int CATCH_FAILED = static_cast(HasReturnvaluesIF::RETURN_FAILED); +} + +namespace tconst { + static constexpr MessageQueueId_t testQueueId = 42; +} + +namespace tglob { + StorageManagerIF* getIpcStoreHandle(); +} + +#endif /* FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ */ diff --git a/unittest/core/CatchRunner.cpp b/unittest/core/CatchRunner.cpp new file mode 100644 index 000000000..35c53cd47 --- /dev/null +++ b/unittest/core/CatchRunner.cpp @@ -0,0 +1,31 @@ +/** + * @file CatchSource.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. + */ + +#ifndef NO_UNIT_TEST_FRAMEWORK + +#define CATCH_CONFIG_RUNNER +#include + +#if CUSTOM_UNITTEST_RUNNER == 0 + +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; +} + +#endif + + +#endif diff --git a/unittest/core/CatchSetup.cpp b/unittest/core/CatchSetup.cpp new file mode 100644 index 000000000..f8543fd2a --- /dev/null +++ b/unittest/core/CatchSetup.cpp @@ -0,0 +1,42 @@ +#include "CatchDefinitions.h" + +#include +#include + + +#ifdef GCOV +#include +#endif + +#include "../../objectmanager/ObjectManager.h" +#include "../../objectmanager/ObjectManagerIF.h" +#include "../../storagemanager/StorageManagerIF.h" +#include "../../datapool/DataPool.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + + +/* Global instantiations normally done in main.cpp */ +/* Initialize Data Pool */ +//namespace glob { +DataPool dataPool(datapool::dataPoolInit); +//} + + +namespace sif { +/* Set up output streams */ +ServiceInterfaceStream debug("DEBUG"); +ServiceInterfaceStream info("INFO"); +ServiceInterfaceStream error("ERROR"); +ServiceInterfaceStream warning("WARNING"); +} + +/* Global object manager */ +ObjectManagerIF *objectManager; + +int customSetup() { + // global setup + objectManager = new ObjectManager(Factory::produce); + objectManager -> initialize(); + return 0; +} + diff --git a/unittest/core/core.mk b/unittest/core/core.mk new file mode 100644 index 000000000..3e5626d33 --- /dev/null +++ b/unittest/core/core.mk @@ -0,0 +1,3 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp) + +INCLUDES += $(CURRENTPATH) \ No newline at end of file diff --git a/unittest/core/printChar.cpp b/unittest/core/printChar.cpp new file mode 100644 index 000000000..755a13b22 --- /dev/null +++ b/unittest/core/printChar.cpp @@ -0,0 +1,10 @@ +#include +#include + +void printChar(const char* character, bool errStream) { + if(errStream) { + std::putc(*character, stderr); + return; + } + std::putc(*character, stdout); +} diff --git a/unittest/core/printChar.h b/unittest/core/printChar.h new file mode 100644 index 000000000..80e867f67 --- /dev/null +++ b/unittest/core/printChar.h @@ -0,0 +1,8 @@ +#ifndef FSFW_UNITTEST_CORE_PRINTCHAR_H_ +#define FSFW_UNITTEST_CORE_PRINTCHAR_H_ + + +extern "C" void printChar(const char*, bool errStream); + + +#endif /* FSFW_UNITTEST_CORE_PRINTCHAR_H_ */ diff --git a/unittest/internal/InternalUnitTester.cpp b/unittest/internal/InternalUnitTester.cpp new file mode 100644 index 000000000..503a71ec3 --- /dev/null +++ b/unittest/internal/InternalUnitTester.cpp @@ -0,0 +1,27 @@ +#include "InternalUnitTester.h" +#include "UnittDefinitions.h" + +#include "osal/IntTestMq.h" +#include "osal/IntTestSemaphore.h" +#include "osal/IntTestMutex.h" +#include "serialize/IntTestSerialization.h" + +#include + +InternalUnitTester::InternalUnitTester() {} + +InternalUnitTester::~InternalUnitTester() {} + +ReturnValue_t InternalUnitTester::performTests() { + sif::info << "Running internal unit tests.." << std::endl; + testserialize::test_serialization(); + testmq::testMq(); + testsemaph::testBinSemaph(); + testsemaph::testCountingSemaph(); + testmutex::testMutex(); + sif::info << "Internal unit tests finished." << std::endl; + return RETURN_OK; +} + + + diff --git a/unittest/internal/InternalUnitTester.h b/unittest/internal/InternalUnitTester.h new file mode 100644 index 000000000..b301b9232 --- /dev/null +++ b/unittest/internal/InternalUnitTester.h @@ -0,0 +1,29 @@ +#ifndef FRAMEWORK_TEST_UNITTESTCLASS_H_ +#define FRAMEWORK_TEST_UNITTESTCLASS_H_ + +#include "UnittDefinitions.h" +#include + +/** + * @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: + InternalUnitTester(); + virtual~ InternalUnitTester(); + + /** + * Some function which calls all other tests + * @return + */ + virtual ReturnValue_t performTests(); +}; + + +#endif /* FRAMEWORK_TEST_UNITTESTCLASS_H_ */ diff --git a/unittest/internal/UnittDefinitions.cpp b/unittest/internal/UnittDefinitions.cpp new file mode 100644 index 000000000..0bdbfcc72 --- /dev/null +++ b/unittest/internal/UnittDefinitions.cpp @@ -0,0 +1,7 @@ +#include + + ReturnValue_t unitt::put_error(std::string errorId) { + sif::error << "Unit Tester error: Failed at test ID " + << errorId << "\n" << std::flush; + return HasReturnvaluesIF::RETURN_FAILED; +} diff --git a/unittest/internal/UnittDefinitions.h b/unittest/internal/UnittDefinitions.h new file mode 100644 index 000000000..ea36fea40 --- /dev/null +++ b/unittest/internal/UnittDefinitions.h @@ -0,0 +1,33 @@ +#ifndef UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ +#define UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ + +#include "../../returnvalues/HasReturnvaluesIF.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include +#include + +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 unitt { +ReturnValue_t put_error(std::string errorId); +} + + + +#endif /* UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ */ diff --git a/unittest/internal/internal.mk b/unittest/internal/internal.mk new file mode 100644 index 000000000..799fd7960 --- /dev/null +++ b/unittest/internal/internal.mk @@ -0,0 +1,3 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/osal/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp) \ No newline at end of file diff --git a/unittest/internal/osal/IntTestMq.cpp b/unittest/internal/osal/IntTestMq.cpp new file mode 100644 index 000000000..630163743 --- /dev/null +++ b/unittest/internal/osal/IntTestMq.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include + +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 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); + } + +} diff --git a/unittest/internal/osal/IntTestMq.h b/unittest/internal/osal/IntTestMq.h new file mode 100644 index 000000000..bc12a9b08 --- /dev/null +++ b/unittest/internal/osal/IntTestMq.h @@ -0,0 +1,9 @@ +#ifndef UNITTEST_INTERNAL_INTESTMQ_H_ +#define UNITTEST_INTERNAL_INTESTMQ_H_ + +namespace testmq { +void testMq(); +} + + +#endif /* UNITTEST_INTERNAL_INTESTMQ_H_ */ diff --git a/unittest/internal/osal/IntTestMutex.cpp b/unittest/internal/osal/IntTestMutex.cpp new file mode 100644 index 000000000..3fd668dfd --- /dev/null +++ b/unittest/internal/osal/IntTestMutex.cpp @@ -0,0 +1,42 @@ +#include "IntTestMutex.h" + +#include +#include + +#if defined(hosted) +#include +#include +#include +#endif + + +void testmutex::testMutex() { + std::string id = "[testMutex]"; + MutexIF* mutex = MutexFactory::instance()->createMutex(); + auto result = mutex->lockMutex(MutexIF::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. +#if defined(hosted) + // hold on, this actually worked ? :-D This calls the function from + // another thread and stores the returnvalue in a future. + auto future = std::async(&MutexIF::lockMutex, mutex, 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); + } + result = mutex->unlockMutex(); + if(result != MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX) { + unitt::put_error(id); + } +} diff --git a/unittest/internal/osal/IntTestMutex.h b/unittest/internal/osal/IntTestMutex.h new file mode 100644 index 000000000..b467ea636 --- /dev/null +++ b/unittest/internal/osal/IntTestMutex.h @@ -0,0 +1,10 @@ +#ifndef UNITTEST_INTERNAL_INTTESTMUTEX_H_ +#define UNITTEST_INTERNAL_INTTESTMUTEX_H_ + +namespace testmutex { +void testMutex(); +} + + + +#endif /* UNITTEST_INTERNAL_INTTESTMUTEX_H_ */ diff --git a/unittest/internal/osal/IntTestSemaphore.cpp b/unittest/internal/osal/IntTestSemaphore.cpp new file mode 100644 index 000000000..534a6a6d5 --- /dev/null +++ b/unittest/internal/osal/IntTestSemaphore.cpp @@ -0,0 +1,160 @@ +#include "IntTestSemaphore.h" +#include +#include +#include +#include + + +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(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(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(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) { +// sif::error << "UnitTester: Semaphore timeout measured incorrect." +// << std::endl; +// 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(abs(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); + } +} diff --git a/unittest/internal/osal/IntTestSemaphore.h b/unittest/internal/osal/IntTestSemaphore.h new file mode 100644 index 000000000..af26c131f --- /dev/null +++ b/unittest/internal/osal/IntTestSemaphore.h @@ -0,0 +1,15 @@ +#ifndef UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ +#define UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ +class SemaphoreIF; +#include + +namespace testsemaph { +void testBinSemaph(); +void testBinSemaphoreImplementation(SemaphoreIF* binSemaph, std::string id); +void testCountingSemaph(); +void testCountingSemaphImplementation(SemaphoreIF* countingSemaph, + std::string id); +} + + +#endif /* UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ */ diff --git a/unittest/internal/serialize/IntTestSerialization.cpp b/unittest/internal/serialize/IntTestSerialization.cpp new file mode 100644 index 000000000..3f231a417 --- /dev/null +++ b/unittest/internal/serialize/IntTestSerialization.cpp @@ -0,0 +1,230 @@ +#include "IntTestSerialization.h" +#include +#include +#include +#include +#include + +using retval = HasReturnvaluesIF; +std::array 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(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint64, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_float, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_double, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sfloat, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sdouble, + const_cast(&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 test_serial_buffer {5, 4, 3, 2, 1}; + SerialBufferAdapter tv_serial_buffer_adapter = + SerialBufferAdapter(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 tv_serial_buffer_adapter2 = + SerialBufferAdapter(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; +} diff --git a/unittest/internal/serialize/IntTestSerialization.h b/unittest/internal/serialize/IntTestSerialization.h new file mode 100644 index 000000000..f8841b820 --- /dev/null +++ b/unittest/internal/serialize/IntTestSerialization.h @@ -0,0 +1,15 @@ +#ifndef UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ +#define UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ +#include +#include + +namespace testserialize { +ReturnValue_t test_serialization(); +ReturnValue_t test_endianness_tools(); +ReturnValue_t test_autoserialization(); +ReturnValue_t test_serial_buffer_adapter(); + +extern std::array test_array; +} + +#endif /* UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */ diff --git a/unittest/lcov.sh b/unittest/lcov.sh new file mode 100644 index 000000000..4db16e5fb --- /dev/null +++ b/unittest/lcov.sh @@ -0,0 +1,3 @@ +#!/bin/bash +lcov --capture --directory . --output-file coverage.info +genhtml coverage.info --output-directory _coverage diff --git a/unittest/testcfg/FSFWConfig.h b/unittest/testcfg/FSFWConfig.h new file mode 100644 index 000000000..4fb224c15 --- /dev/null +++ b/unittest/testcfg/FSFWConfig.h @@ -0,0 +1,46 @@ +#ifndef CONFIG_FSFWCONFIG_H_ +#define CONFIG_FSFWCONFIG_H_ + +#include + +//! Used to determine whether C++ ostreams are used +//! Those can lead to code bloat. +#define FSFW_CPP_OSTREAM_ENABLED 1 + +//! Reduced printout to further decrese code size +//! Be careful, this also turns off most diagnostic prinouts! +#define FSFW_REDUCED_PRINTOUT 0 + +//! Can be used to enable debugging printouts for developing the FSFW +#define FSFW_DEBUGGING 0 + +//! Defines the FIFO depth of each commanding service base which +//! also determines how many commands a CSB service can handle in one cycle +//! simulataneously. This will increase the required RAM for +//! each CSB service ! +#define FSFW_CSB_FIFO_DEPTH 6 + +//! If FSFW_OBJ_EVENT_TRANSLATION is set to one, +//! additional output which requires the translation files translateObjects +//! and translateEvents (and their compiled source files) +#define FSFW_OBJ_EVENT_TRANSLATION 0 + +//! If -DDEBUG is supplied in the build defines, there will be +//! additional output which requires the translation files translateObjects +//! and translateEvents (and their compiles source files) +#if FSFW_OBJ_EVENT_TRANSLATION == 1 +#define FSFW_DEBUG_OUTPUT 1 +//! Specify whether info events are printed too. +#define FSFW_DEBUG_INFO 1 +#include +#include +#else +#define FSFW_DEBUG_OUTPUT 0 +#endif + +//! When using the newlib nano library, C99 support for stdio facilities +//! will not be provided. This define should be set to 1 if this is the case. +#define FSFW_NO_C99_IO 1 + + +#endif /* CONFIG_FSFWCONFIG_H_ */ diff --git a/unittest/testcfg/Makefile-FSFW-Tests b/unittest/testcfg/Makefile-FSFW-Tests new file mode 100644 index 000000000..d43a6edcd --- /dev/null +++ b/unittest/testcfg/Makefile-FSFW-Tests @@ -0,0 +1,415 @@ +#------------------------------------------------------------------------------- +# Makefile for FSFW Test +#------------------------------------------------------------------------------- +# User-modifiable options +#------------------------------------------------------------------------------- +# Fundamentals on the build process of C/C++ Software: +# https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html + +# Make documentation: https://www.gnu.org/software/make/manual/make.pdf +# Online: https://www.gnu.org/software/make/manual/make.html +# General rules: http://make.mad-scientist.net/papers/rules-of-makefiles/#rule3 +SHELL = /bin/sh + +# Chip & board used for compilation +# (can be overriden by adding CHIP=chip and BOARD=board to the command-line) +# Unit Test can only be run on host machine for now (Linux) +FRAMEWORK_PATH = fsfw +FILE_ROOT = $(FRAMEWORK_PATH)/unittest +BOARD = unittest +LINUX = 1 +OS_FSFW = linux +CUSTOM_DEFINES += -D$(OS_FSFW) + +# Copied from stackoverflow, can be used to differentiate between Windows +# and Linux +ifeq ($(OS),Windows_NT) + CUSTOM_DEFINES += -DWIN32 + ifeq ($(PROCESSOR_ARCHITEW6432),AMD64) + CUSTOM_DEFINES += -DAMD64 + else + ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) + CUSTOM_DEFINES += -DAMD64 + endif + ifeq ($(PROCESSOR_ARCHITECTURE),x86) + CUSTOM_DEFINES += -DIA32 + endif + endif +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + DETECTED_OS = LINUX + CUSTOM_DEFINES += -DLINUX + endif + ifeq ($(UNAME_S),Darwin) + CUSTOM_DEFINES += -DOSX + endif + UNAME_P := $(shell uname -p) + ifeq ($(UNAME_P),x86_64) + CUSTOM_DEFINES += -DAMD64 + endif + ifneq ($(filter %86,$(UNAME_P)),) + CUSTOM_DEFINES += -DIA32 + endif + ifneq ($(filter arm%,$(UNAME_P)),) + CUSTOM_DEFINES += -DARM + endif +endif + +UNIT_TEST = 1 +# General folder paths +CONFIG_PATH = $(FILE_ROOT)/config +UNIT_TEST_PATH = $(FILE_ROOT)/tests +CORE_PATH = $(FILE_ROOT)/core + +# Output file basename +BASENAME = fsfw +BINARY_NAME := $(BASENAME)-$(BOARD) +# Output files will be put in this directory inside +OUTPUT_FOLDER = $(OS) + +# Optimization level. Optimized for debugging. +OPTIMIZATION = -O0 + +# Default debug output. Optimized for debugging. +DEBUG_LEVEL = -g3 + +ifdef GCOV +CUSTOM_DEFINES += -DGCOV +endif + + +# Output directories +BUILDPATH = _bin +DEPENDPATH = _dep +OBJECTPATH = _obj + +ifeq ($(MAKECMDGOALS),mission) +BUILD_FOLDER = mission +else +BUILD_FOLDER = devel +endif + +DEPENDDIR = $(DEPENDPATH)/$(OUTPUT_FOLDER)/$(BUILD_FOLDER) +OBJDIR = $(OBJECTPATH)/$(OUTPUT_FOLDER)/$(BUILD_FOLDER) +BINDIR = $(BUILDPATH) + +CLEANDEP = $(DEPENDPATH)/$(OUTPUT_FOLDER) +CLEANOBJ = $(OBJECTPATH)/$(OUTPUT_FOLDER) +CLEANBIN = $(BUILDPATH) +#------------------------------------------------------------------------------- +# Tools and Includes +#------------------------------------------------------------------------------- + +# Tool suffix when cross-compiling +CROSS_COMPILE = + +# C Compiler +CC = $(CROSS_COMPILE)gcc + +# C++ compiler +CXX = $(CROSS_COMPILE)g++ + +# Additional Tools +SIZE = $(CROSS_COMPILE)size +STRIP = $(CROSS_COMPILE)strip +CP = $(CROSS_COMPILE)objcopy + +HEXCOPY = $(CP) -O ihex +BINCOPY = $(CP) -O binary +# files to be compiled, will be filled in by include makefiles +# := assignment is neccessary so we get all paths right +# https://www.gnu.org/software/make/manual/html_node/Flavors.html +CSRC := +CXXSRC := +ASRC := +INCLUDES := + +# Directories where $(directoryname).mk files should be included from +SUBDIRS := $(FRAMEWORK_PATH) $(TEST_PATH) $(UNIT_TEST_PATH) $(CONFIG_PATH) \ + $(CORE_PATH) + + +I_INCLUDES += $(addprefix -I, $(INCLUDES)) + +# This is a hack from http://make.mad-scientist.net/the-eval-function/ +# +# The problem is, that included makefiles should be aware of their relative path +# but not need to guess or hardcode it. So we set $(CURRENTPATH) for them. If +# we do this globally and the included makefiles want to include other makefiles as +# well, they would overwrite $(CURRENTPATH), screwing the include after them. +# +# By using a for-loop with an eval'd macro, we can generate the code to include all +# sub-makefiles (with the correct $(CURRENTPATH) set) before actually evaluating +# (and by this possibly changing $(CURRENTPATH)) them. +# +# This works recursively, if an included makefile wants to include, it can safely set +# $(SUBDIRS) (which has already been evaluated here) and do +# "$(foreach S,$(SUBDIRS),$(eval $(INCLUDE_FILE)))" +# $(SUBDIRS) must be relative to the project root, so to include subdir foo, set +# $(SUBDIRS) = $(CURRENTPATH)/foo. +define INCLUDE_FILE +CURRENTPATH := $S +include $(S)/$(notdir $S).mk +endef +$(foreach S,$(SUBDIRS),$(eval $(INCLUDE_FILE))) + +INCLUDES += $(FILE_ROOT) +INCLUDES += $(FILE_ROOT)/catch2/ + +#------------------------------------------------------------------------------- +# Source Files +#------------------------------------------------------------------------------- + +# All source files which are not includes by the .mk files are added here +# Please ensure that no files are included by both .mk file and here ! + +# if a target is not listed in the current directory, +# make searches in the directories specified with VPATH + +# All C Sources included by .mk files are assigned here +# Add the objects to sources so dependency handling works +C_OBJECTS += $(CSRC:.c=.o) + +# Objects built from Assembly source files +ASM_OBJECTS = $(ASRC:.S=.o) + +# Objects built from C++ source files +CXX_OBJECTS += $(CXXSRC:.cpp=.o) + +#------------------------------------------------------------------------------- +# Build Configuration + Output +#------------------------------------------------------------------------------- + +TARGET = Debug build. +DEBUG_MESSAGE = Off +OPTIMIZATION_MESSAGE = Off + +# Define Messages +MSG_INFO = Software: Hosted unittest \(Catch2\) for the FSFW. +MSG_OPTIMIZATION = Optimization: $(OPTIMIZATION), $(OPTIMIZATION_MESSAGE) +MSG_TARGET = Target Build: $(TARGET) +MSG_DEBUG = Debug level: $(DEBUG_LEVEL), FSFW Debugging: $(DEBUG_MESSAGE) + +MSG_LINKING = Linking: +MSG_COMPILING = Compiling: +MSG_ASSEMBLING = Assembling: +MSG_DEPENDENCY = Collecting dependencies for: +MSG_BINARY = Generate binary: + +# See https://stackoverflow.com/questions/6687630/how-to-remove-unused-c-c-symbols-with-gcc-and-ld +# Used to throw away unused code. Reduces code size significantly ! +# -Wl,--gc-sections: needs to be passed to the linker to throw aways unused code +ifdef KEEP_UNUSED_CODE +PROTOTYPE_OPTIMIZATION = +UNUSED_CODE_REMOVAL = +else +PROTOTYPE_OPTIMIZATION = -ffunction-sections -fdata-sections +UNUSED_CODE_REMOVAL = -Wl,--gc-sections +# Link time optimization +# See https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html for reference +# Link time is larger and size of object files can not be retrieved +# but resulting binary is smaller. Could be used in mission/deployment build +# Requires -ffunction-section in linker call +LINK_TIME_OPTIMIZATION = -flto +OPTIMIZATION += $(PROTOTYPE_OPTIMIZATION) +endif + +# Dependency Flags +# These flags tell the compiler to build dependencies +# See: https://www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html +# Using following guide: http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/#combine +DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPENDDIR)/$*.d + +# Flags for the compiler call +# - std: Which C++ version to use. Common versions: c++11, c++14 and c++17 +# - Wall: enable all warnings +# - Wextra: enable extra warnings +# - g: defines debug level +# - fmessage-length: to control the formatting algorithm for diagnostic messages; +# =0 means no line-wrapping is done; each error message appears on a single line +# - fno-exceptions: stops generating extra code needed to propagate exceptions, +# which can produce significant data size overhead +CUSTOM_DEFINES += -DUNIT_TEST +WARNING_FLAGS = -Wall -Wshadow=local -Wextra -Wimplicit-fallthrough=1 \ + -Wno-unused-parameter + +CXXDEFINES := $(CUSTOM_DEFINES) +CFLAGS += +CXXFLAGS += -I. $(DEBUG_LEVEL) $(WARNING_FLAGS) $(DEPFLAGS) -fmessage-length=0 $(OPTIMIZATION)\ + $(I_INCLUDES) $(CXXDEFINES) +CPPFLAGS += -std=c++11 + +# Flags for the linker call +# LINK_INCLUDES specify the path to used libraries and the linker script +# LINK_LIBRARIES: Link real time support +LDFLAGS := $(DEBUG_LEVEL) $(UNUSED_CODE_REMOVAL) $(OPTIMIZATION) -pthread +LINK_INCLUDES := +LINK_LIBRARIES := + +ifdef LINUX +LINK_LIBRARIES += -lrt +endif + +ifeq ($(OS),Windows_NT) +LINK_LIBRARIES += -lwsock32 -lws2_32 +LDFLASGS += -fuse-ld=lld +endif + +# Gnu Coverage Tools Flags +ifdef GCOV +GCOV_CXXFLAGS = -fprofile-arcs -ftest-coverage --coverage -fno-inline \ + -fno-inline-small-functions -fno-default-inline +CXXFLAGS += $(GCOV_CXXFLAGS) +GCOV_LINKER_LIBS = -lgcov -fprofile-arcs -ftest-coverage +LINK_LIBRARIES += $(GCOV_LINKER_LIBS) +endif + +# $(info $${CXXFLAGS} is [${CXXFLAGS}]) + +#------------------------------------------------------------------------------- +# Rules +#------------------------------------------------------------------------------- +# the call function assigns parameters to temporary variables +# https://www.gnu.org/software/make/manual/make.html#Call-Function +# $(1) = Memory names +# Rules are called for each memory type +# Two Expansion Symbols $$ are to escape the dollar sign for eval. +# See: http://make.mad-scientist.net/the-eval-function/ + +default: all + +# Cleans all files +hardclean: + -rm -rf $(BUILDPATH) + -rm -rf $(OBJECTPATH) + -rm -rf $(DEPENDPATH) + +# Only clean files for current build +clean: + -rm -rf $(CLEANOBJ) + -rm -rf $(CLEANBIN) + -rm -rf $(CLEANDEP) + +# Only clean binaries. Useful for changing the binary type when object files +# are already compiled so complete rebuild is not necessary +cleanbin: + -rm -rf $(CLEANBIN) + +# In this section, the binaries are built for all selected memories +# notestfw: all +all: executable + +# Build target configuration +release: OPTIMIZATION = -Os $(PROTOTYPE_OPTIMIZATION) $(LINK_TIME_OPTIMIZATION) +release: LINK_TIME_OPTIMIZATION = -flto +release: TARGET = Mission build. +release: OPTIMIZATION_MESSAGE = On with Link Time Optimization + +debug: CXXDEFINES += -DDEBUG +debug: TARGET = Debug +debug: DEBUG_MESSAGE = On + +ifndef KEEP_UNUSED_CODE +debug release: OPTIMIZATION_MESSAGE += , no unused code removal +endif + +debug release notestfw: executable + +executable: $(BINDIR)/$(BINARY_NAME).elf + @echo + @echo $(MSG_INFO) + @echo $(MSG_TARGET) + @echo $(MSG_OPTIMIZATION) + @echo $(MSG_DEBUG) + +C_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(C_OBJECTS)) +CXX_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(CXX_OBJECTS)) +ASM_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(ASM_OBJECTS)) +ALL_OBJECTS = $(ASM_OBJECTS_PREFIXED) $(C_OBJECTS_PREFIXED) \ + $(CXX_OBJECTS_PREFIXED) + +# Useful for debugging the Makefile +# Also see: https://www.oreilly.com/openbook/make3/book/ch12.pdf +# $(info $${ALL_OBJECTS} is [${ALL_OBJECTS}]) +# $(info $${CXXSRC} is [${CXXSRC}]) + +# Automatic variables are used here extensively. Some of them +# are escaped($$) to suppress immediate evaluation. The most important ones are: +# $@: Name of Target (left side of rule) +# $<: Name of the first prerequisite (right side of rule) +# @^: List of all prerequisite, omitting duplicates +# @D: Directory and file-within-directory part of $@ + +# Generates binary and displays all build properties +# -p with mkdir ignores error and creates directory when needed. + +# SHOW_DETAILS = 1 + + +# Link with required libraries: HAL (Hardware Abstraction Layer) and +# HCC (File System Library) +$(BINDIR)/$(BINARY_NAME).elf: $(ALL_OBJECTS) + @echo + @echo $(MSG_LINKING) Target $@ + @mkdir -p $(@D) +ifdef SHOW_DETAILS + $(CXX) $(LDFLAGS) $(LINK_INCLUDES) -o $@ $^ $(LINK_LIBRARIES) +else + @$(CXX) $(LDFLAGS) $(LINK_INCLUDES) -o $@ $^ $(LINK_LIBRARIES) +endif +ifeq ($(BUILD_FOLDER), mission) +# With Link Time Optimization, section size is not available + $(SIZE) $@ +else + $(SIZE) $^ $@ +endif + +$(BINDIR)/$(BINARY_NAME).hex: $(BINDIR)/$(BINARY_NAME).elf + @echo + @echo $(MSG_BINARY) + @mkdir -p $(@D) + $(HEXCOPY) $< $@ + +# Build new objects for changed dependencies. +$(OBJDIR)/%.o: %.cpp +$(OBJDIR)/%.o: %.cpp $(DEPENDDIR)/%.d | $(DEPENDDIR) + @echo + @echo $(MSG_COMPILING) $< + @mkdir -p $(@D) +ifdef SHOW_DETAILS + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< +else + @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< +endif + +$(OBJDIR)/%.o: %.c +$(OBJDIR)/%.o: %.c $(DEPENDDIR)/%.d | $(DEPENDDIR) + @echo + @echo $(MSG_COMPILING) $< + @mkdir -p $(@D) +ifdef SHOW_DETAILS + $(CC) $(CXXFLAGS) $(CFLAGS) -c -o $@ $< +else + @$(CC) $(CXXFLAGS) $(CFLAGS) -c -o $@ $< +endif + +#------------------------------------------------------------------------------- +# Dependency Handling +#------------------------------------------------------------------------------- + +# Dependency Handling according to following guide: +# http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ +$(DEPENDDIR): + @mkdir -p $(@D) +DEPENDENCY_RELATIVE = $(CSRC:.c=.d) $(CXXSRC:.cpp=.d) +# This is the list of all dependencies +DEPFILES = $(addprefix $(DEPENDDIR)/, $(DEPENDENCY_RELATIVE)) +# Create subdirectories for dependencies +$(DEPFILES): + @mkdir -p $(@D) +# Include all dependencies +include $(wildcard $(DEPFILES)) + +# .PHONY tells make that these targets aren't files +.PHONY: clean release debug all hardclean cleanbin diff --git a/unittest/testcfg/TestsConfig.h b/unittest/testcfg/TestsConfig.h new file mode 100644 index 000000000..cd967fa73 --- /dev/null +++ b/unittest/testcfg/TestsConfig.h @@ -0,0 +1,8 @@ +#ifndef FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ +#define FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ + + +#define CUSTOM_UNITTEST_RUNNER 0 + + +#endif /* FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ */ diff --git a/unittest/testcfg/cdatapool/dataPoolInit.cpp b/unittest/testcfg/cdatapool/dataPoolInit.cpp new file mode 100644 index 000000000..ad2dc4ef5 --- /dev/null +++ b/unittest/testcfg/cdatapool/dataPoolInit.cpp @@ -0,0 +1,5 @@ +#include "dataPoolInit.h" + +void datapool::dataPoolInit(std::map * poolMap) { + +} diff --git a/unittest/testcfg/cdatapool/dataPoolInit.h b/unittest/testcfg/cdatapool/dataPoolInit.h new file mode 100644 index 000000000..23a3d01f0 --- /dev/null +++ b/unittest/testcfg/cdatapool/dataPoolInit.h @@ -0,0 +1,17 @@ +#ifndef HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_ +#define HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_ + +#include +#include +#include +#include + + +namespace datapool { + void dataPoolInit(std::map * poolMap); + + enum datapoolvariables { + NO_PARAMETER = 0, + }; +} +#endif /* CONFIG_CDATAPOOL_DATAPOOLINIT_H_ */ diff --git a/unittest/testcfg/devices/logicalAddresses.cpp b/unittest/testcfg/devices/logicalAddresses.cpp new file mode 100644 index 000000000..c7ce314d1 --- /dev/null +++ b/unittest/testcfg/devices/logicalAddresses.cpp @@ -0,0 +1,5 @@ +#include "logicalAddresses.h" + + + + diff --git a/unittest/testcfg/devices/logicalAddresses.h b/unittest/testcfg/devices/logicalAddresses.h new file mode 100644 index 000000000..cdf870252 --- /dev/null +++ b/unittest/testcfg/devices/logicalAddresses.h @@ -0,0 +1,15 @@ +#ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_ +#define CONFIG_DEVICES_LOGICALADDRESSES_H_ + +#include +#include +#include + +namespace addresses { + /* Logical addresses have uint32_t datatype */ + enum logicalAddresses: address_t { + }; +} + + +#endif /* CONFIG_DEVICES_LOGICALADDRESSES_H_ */ diff --git a/unittest/testcfg/devices/powerSwitcherList.cpp b/unittest/testcfg/devices/powerSwitcherList.cpp new file mode 100644 index 000000000..343f78d04 --- /dev/null +++ b/unittest/testcfg/devices/powerSwitcherList.cpp @@ -0,0 +1,4 @@ +#include "powerSwitcherList.h" + + + diff --git a/unittest/testcfg/devices/powerSwitcherList.h b/unittest/testcfg/devices/powerSwitcherList.h new file mode 100644 index 000000000..86ddea570 --- /dev/null +++ b/unittest/testcfg/devices/powerSwitcherList.h @@ -0,0 +1,12 @@ +#ifndef CONFIG_DEVICES_POWERSWITCHERLIST_H_ +#define CONFIG_DEVICES_POWERSWITCHERLIST_H_ + +namespace switches { + /* Switches are uint8_t datatype and go from 0 to 255 */ + enum switcherList { + }; + +} + + +#endif /* CONFIG_DEVICES_POWERSWITCHERLIST_H_ */ diff --git a/unittest/testcfg/events/subsystemIdRanges.h b/unittest/testcfg/events/subsystemIdRanges.h new file mode 100644 index 000000000..24eee819f --- /dev/null +++ b/unittest/testcfg/events/subsystemIdRanges.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ +#define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ + +#include +#include + +/** + * @brief Custom subsystem IDs can be added here + * @details + * Subsystem IDs are used to create unique events. + */ +namespace SUBSYSTEM_ID { +enum: uint8_t { + SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE, +}; +} + +#endif /* CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ */ diff --git a/unittest/testcfg/ipc/MissionMessageTypes.cpp b/unittest/testcfg/ipc/MissionMessageTypes.cpp new file mode 100644 index 000000000..d68cb58cc --- /dev/null +++ b/unittest/testcfg/ipc/MissionMessageTypes.cpp @@ -0,0 +1,12 @@ +#include "MissionMessageTypes.h" + +#include + +void messagetypes::clearMissionMessage(CommandMessage* message) { + switch(message->getMessageType()) { + default: + break; + } +} + + diff --git a/unittest/testcfg/ipc/MissionMessageTypes.h b/unittest/testcfg/ipc/MissionMessageTypes.h new file mode 100644 index 000000000..832d8e583 --- /dev/null +++ b/unittest/testcfg/ipc/MissionMessageTypes.h @@ -0,0 +1,22 @@ +#ifndef CONFIG_IPC_MISSIONMESSAGETYPES_H_ +#define CONFIG_IPC_MISSIONMESSAGETYPES_H_ + +#include + +class CommandMessage; + +/** + * Custom command messages are specified here. + * Most messages needed to use FSFW are already located in + * + * @param message Generic Command Message + */ +namespace messagetypes{ +enum MESSAGE_TYPE { + MISSION_MESSAGE_TYPE_START = FW_MESSAGES_COUNT, +}; + +void clearMissionMessage(CommandMessage* message); +} + +#endif /* CONFIG_IPC_MISSIONMESSAGETYPES_H_ */ diff --git a/unittest/testcfg/objects/Factory.cpp b/unittest/testcfg/objects/Factory.cpp new file mode 100644 index 000000000..e05b7942e --- /dev/null +++ b/unittest/testcfg/objects/Factory.cpp @@ -0,0 +1,34 @@ +#include "Factory.h" + +#include +#include + +#include +#include + +/** + * @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(object_id) function from the ObjectManagerIF. + * + * Framework objects are created first. + * + * @ingroup init + */ +void Factory::produce(void) { + setStaticFrameworkObjectIds(); + new EventManager(objects::EVENT_MANAGER); + new HealthTable(objects::HEALTH_TABLE); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + +} + +void Factory::setStaticFrameworkObjectIds() { + +} + + diff --git a/unittest/testcfg/objects/Factory.h b/unittest/testcfg/objects/Factory.h new file mode 100644 index 000000000..84f9207e2 --- /dev/null +++ b/unittest/testcfg/objects/Factory.h @@ -0,0 +1,16 @@ +#ifndef FACTORY_H_ +#define FACTORY_H_ + +#include + +namespace Factory { + /** + * @brief Creates all SystemObject elements which are persistent + * during execution. + */ + void produce(); + void setStaticFrameworkObjectIds(); + +} + +#endif /* FACTORY_H_ */ diff --git a/unittest/testcfg/objects/systemObjectList.h b/unittest/testcfg/objects/systemObjectList.h new file mode 100644 index 000000000..0e034aff8 --- /dev/null +++ b/unittest/testcfg/objects/systemObjectList.h @@ -0,0 +1,16 @@ +#ifndef HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ +#define HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ + +#include +#include + +// The objects will be instantiated in the ID order +namespace objects { + enum sourceObjects: uint32_t { + /* All addresses between start and end are reserved for the FSFW */ + FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION, + FSFW_CONFIG_RESERVED_END = TM_STORE + }; +} + +#endif /* BSP_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ */ diff --git a/unittest/testcfg/pollingsequence/PollingSequenceFactory.cpp b/unittest/testcfg/pollingsequence/PollingSequenceFactory.cpp new file mode 100644 index 000000000..f836a7462 --- /dev/null +++ b/unittest/testcfg/pollingsequence/PollingSequenceFactory.cpp @@ -0,0 +1,23 @@ +#include "PollingSequenceFactory.h" + +#include +#include +#include + +ReturnValue_t pst::pollingSequenceInitDefault( + FixedTimeslotTaskIF *thisSequence) { + /* Length of a communication cycle */ + uint32_t length = thisSequence->getPeriodMs(); + + /* Add polling sequence table here */ + + if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) { + return HasReturnvaluesIF::RETURN_OK; + } + else { + sif::error << "pst::pollingSequenceInitDefault: Sequence invalid!" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } +} + diff --git a/unittest/testcfg/pollingsequence/PollingSequenceFactory.h b/unittest/testcfg/pollingsequence/PollingSequenceFactory.h new file mode 100644 index 000000000..c5d41b7d8 --- /dev/null +++ b/unittest/testcfg/pollingsequence/PollingSequenceFactory.h @@ -0,0 +1,32 @@ +#ifndef POLLINGSEQUENCEFACTORY_H_ +#define POLLINGSEQUENCEFACTORY_H_ + +#include + +class FixedTimeslotTaskIF; + +/** + * All device handlers are scheduled by adding them into Polling Sequence Tables (PST) + * to satisfy stricter timing requirements of device communication, + * A device handler has four different communication steps: + * 1. DeviceHandlerIF::SEND_WRITE -> Send write via interface + * 2. DeviceHandlerIF::GET_WRITE -> Get confirmation for write + * 3. DeviceHandlerIF::SEND_READ -> Send read request + * 4. DeviceHandlerIF::GET_READ -> Read from interface + * The PST specifies precisely when the respective ComIF functions are called + * during the communication cycle time. + * The task is created using the FixedTimeslotTaskIF, + * which utilises the underlying Operating System Abstraction Layer (OSAL) + * + * @param thisSequence FixedTimeslotTaskIF * object is passed inside the Factory class when creating the PST + * @return + */ +namespace pst { + +/* Default PST */ +ReturnValue_t pollingSequenceInitDefault(FixedTimeslotTaskIF *thisSequence); + + +} + +#endif /* POLLINGSEQUENCEINIT_H_ */ diff --git a/unittest/testcfg/returnvalues/classIds.h b/unittest/testcfg/returnvalues/classIds.h new file mode 100644 index 000000000..606cc60b2 --- /dev/null +++ b/unittest/testcfg/returnvalues/classIds.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_RETURNVALUES_CLASSIDS_H_ +#define CONFIG_RETURNVALUES_CLASSIDS_H_ + +#include + +/** + * @brief CLASS_ID defintions which are required for custom returnvalues. + */ +namespace CLASS_ID { +enum { + MISSION_CLASS_ID_START = FW_CLASS_ID_COUNT, +}; +} + + +#endif /* CONFIG_RETURNVALUES_CLASSIDS_H_ */ diff --git a/unittest/testcfg/testcfg.mk b/unittest/testcfg/testcfg.mk new file mode 100644 index 000000000..64fa87f65 --- /dev/null +++ b/unittest/testcfg/testcfg.mk @@ -0,0 +1,15 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/cdatapool/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp) + +INCLUDES += $(CURRENTPATH) +INCLUDES += $(CURRENTPATH)/objects +INCLUDES += $(CURRENTPATH)/ipc +INCLUDES += $(CURRENTPATH)/pollingsequence +INCLUDES += $(CURRENTPATH)/returnvalues +INCLUDES += $(CURRENTPATH)/tmtc +INCLUDES += $(CURRENTPATH)/events +INCLUDES += $(CURRENTPATH)/devices +INCLUDES += $(CURRENTPATH)/cdatapool diff --git a/unittest/testcfg/tmtc/apid.h b/unittest/testcfg/tmtc/apid.h new file mode 100644 index 000000000..c0231bca8 --- /dev/null +++ b/unittest/testcfg/tmtc/apid.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_TMTC_APID_H_ +#define CONFIG_TMTC_APID_H_ + +#include + +/** + * Application Process Definition: entity, uniquely identified by an + * application process ID (APID), capable of generating telemetry source + * packets and receiving telecommand packets. + * + * Chose APID(s) for mission and define it here. + */ +namespace apid { + static const uint16_t DEFAULT_APID = 0x00; +} + + +#endif /* CONFIG_TMTC_APID_H_ */ diff --git a/unittest/testcfg/tmtc/pusIds.h b/unittest/testcfg/tmtc/pusIds.h new file mode 100644 index 000000000..821a9982a --- /dev/null +++ b/unittest/testcfg/tmtc/pusIds.h @@ -0,0 +1,25 @@ +#ifndef CONFIG_TMTC_PUSIDS_HPP_ +#define CONFIG_TMTC_PUSIDS_HPP_ + +#include + +namespace pus { +enum Ids: uint8_t { + PUS_SERVICE_1 = 1, + PUS_SERVICE_2 = 2, + PUS_SERVICE_3 = 3, + PUS_SERVICE_5 = 5, + PUS_SERVICE_6 = 6, + PUS_SERVICE_8 = 8, + PUS_SERVICE_9 = 9, + PUS_SERVICE_11 = 11, + PUS_SERVICE_17 = 17, + PUS_SERVICE_19 = 19, + PUS_SERVICE_20 = 20, + PUS_SERVICE_23 = 23, + PUS_SERVICE_200 = 200, + PUS_SERVICE_201 = 201, +}; +}; + +#endif /* CONFIG_TMTC_PUSIDS_HPP_ */ diff --git a/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp new file mode 100644 index 000000000..d5b2e467b --- /dev/null +++ b/unittest/tests/action/TestActionHelper.cpp @@ -0,0 +1,106 @@ +//#include "TestActionHelper.h" +//#include +//#include +//#include +//#include "../../core/CatchDefinitions.h" +// +// +//TEST_CASE( "Action Helper" , "[ActionHelper]") { +// ActionHelperOwnerMockBase testDhMock; +// MessageQueueMockBase testMqMock; +// ActionHelper actionHelper = ActionHelper( +// &testDhMock, dynamic_cast(&testMqMock)); +// CommandMessage actionMessage; +// ActionId_t testActionId = 777; +// std::array testParams {1, 2, 3}; +// store_address_t paramAddress; +// StorageManagerIF *ipcStore = tglob::getIpcStoreHandle(); +// ipcStore->addData(¶mAddress, 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(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(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(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); +// REQUIRE(testMessage.getParameter() == static_cast(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(testMqMock.getId(), testActionId, status); +// CHECK(testMqMock.wasMessageSent()); +// CommandMessage testMessage; +// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::COMPLETION_FAILED)); +// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); +// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(status)); +// } +// +// SECTION("Handle failed"){ +// store_address_t toLongParamAddress = StorageManagerIF::INVALID_ADDRESS; +// std::array 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(StorageManagerIF::DATA_DOES_NOT_EXIST)); +// CommandMessage testMessage; +// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(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(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); +// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(StorageManagerIF::ILLEGAL_STORAGE_ID)); +// REQUIRE(ActionMessage::getStep(&testMessage) == 0); +// } +// +// +// SECTION("Data Reply"){ +// +// } +//} diff --git a/unittest/tests/action/TestActionHelper.h b/unittest/tests/action/TestActionHelper.h new file mode 100644 index 000000000..9bc93d3e1 --- /dev/null +++ b/unittest/tests/action/TestActionHelper.h @@ -0,0 +1,131 @@ +//#ifndef UNITTEST_HOSTED_TESTACTIONHELPER_H_ +//#define UNITTEST_HOSTED_TESTACTIONHELPER_H_ +// +//#include +//#include +//#include +//#include +// +// +//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; isize; +// } +// if(ptr != nullptr){ +// *ptr = buffer; +// } +// } +//}; +// +// +//class MessageQueueMockBase: public MessageQueueIF { +//public: +// MessageQueueId_t myQueueId = 0; +// bool defaultDestSet = false; +// bool messageSent = false; +// +// +// +// bool wasMessageSent() { +// bool tempMessageSent = messageSent; +// messageSent = false; +// return tempMessageSent; +// } +// +// virtual ReturnValue_t reply( MessageQueueMessage* message ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// }; +// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message, +// MessageQueueId_t *receivedFrom) { +// (*message) = lastMessage; +// lastMessage.clear(); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message) { +// (*message) = lastMessage; +// lastMessage.clear(); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t flush(uint32_t* count) { +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual MessageQueueId_t getLastPartner() const { +// return tconst::testQueueId; +// } +// virtual MessageQueueId_t getId() const { +// return tconst::testQueueId; +// } +// virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, +// MessageQueueMessage* message, MessageQueueId_t sentFrom, +// bool ignoreFault = false ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, +// MessageQueueMessage* message, bool ignoreFault = false ) override { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, +// MessageQueueId_t sentFrom, bool ignoreFault = false ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t sendToDefault( MessageQueueMessage* message ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { +// myQueueId = defaultDestination; +// defaultDestSet = true; +// } +// +// virtual MessageQueueId_t getDefaultDestination() const { +// return myQueueId; +// } +// virtual bool isDefaultDestinationSet() const { +// return defaultDestSet; +// } +//private: +// MessageQueueMessage lastMessage; +// +//}; +// +// +//#endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */ diff --git a/unittest/tests/container/RingBufferTest.cpp b/unittest/tests/container/RingBufferTest.cpp new file mode 100644 index 000000000..8b82d407f --- /dev/null +++ b/unittest/tests/container/RingBufferTest.cpp @@ -0,0 +1,327 @@ +#include +#include +#include "../../core/CatchDefinitions.h" + +#include + +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[10]; + 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[10]; + 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); + } +} diff --git a/unittest/tests/container/TestArrayList.cpp b/unittest/tests/container/TestArrayList.cpp new file mode 100644 index 000000000..914188cb7 --- /dev/null +++ b/unittest/tests/container/TestArrayList.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include "../../core/CatchDefinitions.h" + +/** + * @brief Array List test + */ +TEST_CASE("Array List" , "[ArrayListTest]") { + //perform set-up here + ArrayList 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 complexList(20); + SECTION("SimpleTest") { + REQUIRE(list.maxSize()==20); + REQUIRE(list.size == 0); + REQUIRE(list.insert(10) == static_cast(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*>(&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(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(list.insert(20) == static_cast(ArrayList::FULL)); + ArrayList::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::Iterator it = list.begin(); + for (auto i =0; i < 10; i++){ + REQUIRE(list.insert(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + it++; + const uint16_t* number = it.value; + REQUIRE(*number == 1); + } + + SECTION("Const Iterator"){ + ArrayList::Iterator it = complexList.begin(); + for (auto i =0; i < 10; i++){ + REQUIRE(complexList.insert(TestClass(i, i+1)) == static_cast(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::Iterator it4(&(complexList[2])); + REQUIRE(it4->number1 == 2); + REQUIRE((*it4).number2 == 3); + REQUIRE(complexList.remaining()==10); + } +} diff --git a/unittest/tests/container/TestDynamicFifo.cpp b/unittest/tests/container/TestDynamicFifo.cpp new file mode 100644 index 000000000..6c9b7415d --- /dev/null +++ b/unittest/tests/container/TestDynamicFifo.cpp @@ -0,0 +1,149 @@ + +#include +#include +#include + +#include +#include + +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 fifo(3); + std::vector 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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(FIFOBase::FULL)); + + struct Test testptr; + REQUIRE(fifo.peek(&testptr)==static_cast(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(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(FIFOBase::EMPTY)); + REQUIRE(fifo.peek(&testptr)==static_cast(FIFOBase::EMPTY)); + REQUIRE(not fifo.full()); + REQUIRE(fifo.empty()); + REQUIRE(fifo.pop()==static_cast(FIFOBase::EMPTY)); + + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==2); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + REQUIRE(fifo.peek(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + equal = testptr == structTwo; + REQUIRE(equal); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==0); + REQUIRE(fifo.empty()); + //struct Test* ptr = nullptr; + //REQUIRE(fifo.retrieve(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + //REQUIRE(fifo.peek(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + }; + SECTION("Copy Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + DynamicFIFO fifo2(fifo); + REQUIRE(fifo2.size()==3); + REQUIRE(fifo2.full()); + REQUIRE(not fifo2.empty()); + + }; + + SECTION("Assignment Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + DynamicFIFO 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(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + + }; + + SECTION("Assignment Test Smaller"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + DynamicFIFO 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(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + }; + +}; diff --git a/unittest/tests/container/TestFifo.cpp b/unittest/tests/container/TestFifo.cpp new file mode 100644 index 000000000..3775f4242 --- /dev/null +++ b/unittest/tests/container/TestFifo.cpp @@ -0,0 +1,138 @@ + +#include +#include +#include + +#include +#include "../../core/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 fifo; + std::vector 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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(FIFOBase::FULL)); + + struct Test testptr; + REQUIRE(fifo.peek(&testptr)==static_cast(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(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(FIFOBase::EMPTY)); + REQUIRE(fifo.peek(&testptr)==static_cast(FIFOBase::EMPTY)); + REQUIRE(not fifo.full()); + REQUIRE(fifo.empty()); + REQUIRE(fifo.pop()==static_cast(FIFOBase::EMPTY)); + + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==2); + REQUIRE(fifo.pop()==static_cast(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(HasReturnvaluesIF::RETURN_FAILED)); + REQUIRE(fifo.peek(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + + + REQUIRE(fifo.peek(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + equal = testptr == structTwo; + REQUIRE(equal); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==0); + REQUIRE(fifo.empty()); + + + }; + SECTION("Copy Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + FIFO 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(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + + }; + + SECTION("Assignment Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + FIFO 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(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + }; +}; diff --git a/unittest/tests/container/TestFixedArrayList.cpp b/unittest/tests/container/TestFixedArrayList.cpp new file mode 100644 index 000000000..737932e36 --- /dev/null +++ b/unittest/tests/container/TestFixedArrayList.cpp @@ -0,0 +1,42 @@ +#include "../../core/CatchDefinitions.h" + +#include +#include + +#include + + +TEST_CASE( "FixedArrayList Tests", "[TestFixedArrayList]") { + INFO("FixedArrayList Tests"); + using testList = FixedArrayList; + testList list; + REQUIRE(list.size==0); + REQUIRE(list.insert(10) == static_cast(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(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(list.insert(260) == static_cast(ArrayList::FULL)); + list.clear(); + REQUIRE(list.size == 0); + } +} + + + diff --git a/unittest/tests/container/TestFixedMap.cpp b/unittest/tests/container/TestFixedMap.cpp new file mode 100644 index 000000000..079062f00 --- /dev/null +++ b/unittest/tests/container/TestFixedMap.cpp @@ -0,0 +1,172 @@ +#include +#include + +#include +#include "../../core/CatchDefinitions.h" + +template class FixedMap; + +TEST_CASE( "FixedMap Tests", "[TestFixedMap]") { + INFO("FixedMap Tests"); + + FixedMap 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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.exists(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(i)->second==i+1); + REQUIRE(not map.empty()); + } + REQUIRE(map.insert(0, 0) == static_cast(FixedMap::KEY_ALREADY_EXISTS)); + REQUIRE(map.insert(31, 0) == static_cast(FixedMap::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 30); + REQUIRE(map.full()); + { + uint16_t* ptr; + REQUIRE(map.find(5,&ptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(*ptr == 6); + REQUIRE(*(map.findValue(6)) == 7); + REQUIRE(map.find(31,&ptr) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + } + + REQUIRE(map.getSerializedSize() == (sizeof(uint32_t)+ 30*(sizeof(uint32_t) + sizeof(uint16_t)))); + REQUIRE(map.erase(2) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.erase(31) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 29); + + for (auto element: map){ + if (element.first == 5){ + REQUIRE(element.second == 6); + } + } + + for (FixedMap::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::Iterator it = map.begin(); it != map.end(); it++){ + REQUIRE(map.erase(&it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + + REQUIRE(map.size() == 0); + + for (FixedMap::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::Iterator it = map.end(); + REQUIRE(map.insert(36, 37, &it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(it->first == 36); + REQUIRE(it->second == 37); + REQUIRE(map.size() == 1); + REQUIRE(map.insert(37, 38, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(37)->second == 38); + REQUIRE(map.size() == 2); + REQUIRE(map.insert(37, 24, nullptr) == static_cast(FixedMap::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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.insert(37, 38, nullptr) == static_cast(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(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(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(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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(first_element == i); + uint16_t second_element = 0; + REQUIRE( + SerializeAdapter::deSerialize(&second_element, &ptr2, &size, + SerializeIF::Endianness::BIG) + == static_cast(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(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::Iterator it; + std::pair pair = std::make_pair(44, 43); + it = FixedMap::Iterator(&pair); + REQUIRE(map.erase(&it) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.find(45) == map.end()); + size_t toLargeMap = 100; + const uint8_t* ptr = reinterpret_cast(&toLargeMap); + size_t size = sizeof(size_t); + REQUIRE(map.deSerialize(&ptr, &size, SerializeIF::Endianness::BIG) == + static_cast(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(HasReturnvaluesIF::RETURN_OK)); + map.clear(); + REQUIRE(map.size()==0); + const uint8_t* ptr2 = newBuffer; + REQUIRE(map.deSerialize(&ptr2, &size, + SerializeIF::Endianness::LITTLE) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.size()==1); + REQUIRE(map.find(10)->second == 20); + }; +} diff --git a/unittest/tests/container/TestFixedOrderedMultimap.cpp b/unittest/tests/container/TestFixedOrderedMultimap.cpp new file mode 100644 index 000000000..95194cb5a --- /dev/null +++ b/unittest/tests/container/TestFixedOrderedMultimap.cpp @@ -0,0 +1,203 @@ +#include +#include + +#include +#include "../../core/CatchDefinitions.h" + +TEST_CASE( "FixedOrderedMultimap Tests", "[TestFixedOrderedMultimap]") { + INFO("FixedOrderedMultimap Tests"); + + FixedOrderedMultimap 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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.exists(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(i)->second==i+1); + } + REQUIRE(map.insert(0, 0) == static_cast(FixedOrderedMultimap::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 30); + { + uint16_t* ptr; + REQUIRE(map.find(5,&ptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(*ptr == 6); + REQUIRE(map.find(31,&ptr) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + } + REQUIRE(map.erase(2) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.erase(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 29); + + for (auto element: map){ + if (element.first == 5){ + REQUIRE(element.second == 6); + } + } + + for (FixedOrderedMultimap::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::Iterator it = map.begin(); + while(it != map.end()){ + REQUIRE(map.erase(&it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(map.size() == 0); + } + + for (FixedOrderedMultimap::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::Iterator it = map.end(); + REQUIRE(map.insert(36, 37, &it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(it->first == 36); + REQUIRE(it->second == 37); + REQUIRE(map.size() == 1); + REQUIRE(map.insert(37, 38, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(37)->second == 38); + REQUIRE(map.size() == 2); + REQUIRE(map.insert(37, 24, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(37)->second == 38); + REQUIRE(map.insert(0, 1, nullptr) == static_cast(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::Iterator it; + it = map.end(); + REQUIRE(map.erase(&it) == static_cast(FixedOrderedMultimap::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 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(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.exists(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + bool compare = map.find(i)->second == TestClass(i+1,i); + REQUIRE(compare); + } + REQUIRE(map.insert(0, TestClass()) == static_cast(FixedOrderedMultimap::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 30); + { + TestClass* ptr = nullptr; + REQUIRE(map.find(5,&ptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + bool compare = *ptr == TestClass(6, 5); + REQUIRE(compare); + REQUIRE(map.find(31,&ptr) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + } + REQUIRE(map.erase(2) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.erase(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(FixedOrderedMultimap::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::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::Iterator it = map.begin(); + while(it != map.end()){ + REQUIRE(map.erase(&it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(map.size() == 0); + } + + for (FixedOrderedMultimap::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::Iterator it = map.end(); + REQUIRE(map.insert(36, TestClass(37, 36), &it) == static_cast(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(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(HasReturnvaluesIF::RETURN_OK)); + compare = map.find(37)->second == TestClass(38, 37); + REQUIRE(compare); + REQUIRE(map.insert(0, TestClass(1, 0), nullptr) == static_cast(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::Iterator it; + it = map.end(); + REQUIRE(map.erase(&it) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.find(1)== map.end()); + } +} diff --git a/unittest/tests/container/TestPlacementFactory.cpp b/unittest/tests/container/TestPlacementFactory.cpp new file mode 100644 index 000000000..5edbb9d22 --- /dev/null +++ b/unittest/tests/container/TestPlacementFactory.cpp @@ -0,0 +1,45 @@ +//#include +//#include +//#include +//#include +// +//#include +//#include "../../core/CatchDefinitions.h" +// +//TEST_CASE( "PlacementFactory Tests", "[TestPlacementFactory]") { +// INFO("PlacementFactory Tests"); +// +// 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<3> storagePool(0x1, element_sizes, n_elements, false, true); +// PlacementFactory factory(&storagePool); +// +// SECTION("Pool overload"){ +// store_address_t address; +// uint8_t* ptr = nullptr; +// REQUIRE(storagePool.getFreeElement(&address, sizeof(ArrayList), &ptr) +// == static_cast(StorageManagerIF::DATA_TOO_LARGE)); +// ArrayList* list2 = factory.generate >(80); +// REQUIRE(list2 == nullptr); +// } +// +// SECTION("Test generate and destroy"){ +// uint64_t* number = factory.generate(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(StorageManagerIF::DATA_TOO_LARGE)); +// uint64_t* number2 = factory.generate(12345); +// REQUIRE(number2 == nullptr); +// REQUIRE(factory.destroy(number) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr) +// == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(storagePool.deleteData(address) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// +// //Check that PlacementFactory checks for nullptr +// ptr = nullptr; +// REQUIRE(factory.destroy(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); +// } +//} diff --git a/unittest/tests/osal/TestMessageQueue.cpp b/unittest/tests/osal/TestMessageQueue.cpp new file mode 100644 index 000000000..8e59fa084 --- /dev/null +++ b/unittest/tests/osal/TestMessageQueue.cpp @@ -0,0 +1,40 @@ +#include +#include +#include "catch.hpp" +#include +#include "core/CatchDefinitions.h" + +TEST_CASE("MessageQueue Basic Test","[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 testData { 0 }; + testData[0] = 42; + MessageQueueMessage testMessage(testData.data(), 1); + testSenderMq->setDefaultDestination(testReceiverMqId); + + SECTION("Simple Tests") { + auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage); + REQUIRE(result == retval::CATCH_OK); + MessageQueueMessage recvMessage; + result = testReceiverMq->receiveMessage(&recvMessage); + REQUIRE(result == retval::CATCH_OK); + CHECK(recvMessage.getData()[0] == 42); + + result = testSenderMq->sendMessage(testReceiverMqId, &testMessage); + REQUIRE(result == retval::CATCH_OK); + MessageQueueId_t senderId = 0; + result = testReceiverMq->receiveMessage(&recvMessage,&senderId); + REQUIRE(result == retval::CATCH_OK); + CHECK(recvMessage.getData()[0] == 42); + CHECK(senderId == testSenderMqId); + senderId = testReceiverMq->getLastPartner(); + CHECK(senderId == testSenderMqId); + } + + +} diff --git a/unittest/tests/osal/TestSemaphore.cpp b/unittest/tests/osal/TestSemaphore.cpp new file mode 100644 index 000000000..f7b255346 --- /dev/null +++ b/unittest/tests/osal/TestSemaphore.cpp @@ -0,0 +1,46 @@ + +#ifdef LINUX + +/* +#include "core/CatchDefinitions.h" +#include "catch.hpp" + +#include +#include + +TEST_CASE("Binary Semaphore Test" , "[BinSemaphore]") { + //perform set-up here + SemaphoreIF* binSemaph = SemaphoreFactory::instance()-> + createBinarySemaphore(); + REQUIRE(binSemaph != nullptr); + SECTION("Simple Test") { + // set-up is run for each section + REQUIRE(binSemaph->getSemaphoreCounter() == 1); + REQUIRE(binSemaph->release() == + static_cast(SemaphoreIF::SEMAPHORE_NOT_OWNED)); + REQUIRE(binSemaph->acquire(SemaphoreIF::POLLING) == + retval::CATCH_OK); + { + // not precise enough on linux.. should use clock instead.. + //Stopwatch stopwatch(false); + //REQUIRE(binSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 5) == + // SemaphoreIF::SEMAPHORE_TIMEOUT); + //dur_millis_t time = stopwatch.stop(); + //CHECK(time == 5); + } + REQUIRE(binSemaph->getSemaphoreCounter() == 0); + REQUIRE(binSemaph->release() == retval::CATCH_OK); + } + SemaphoreFactory::instance()->deleteSemaphore(binSemaph); + // perform tear-down here +} + + +TEST_CASE("Counting Semaphore Test" , "[CountingSemaph]") { + SECTION("Simple Test") { + + } +} +*/ + +#endif diff --git a/unittest/tests/serialize/TestSerialBufferAdapter.cpp b/unittest/tests/serialize/TestSerialBufferAdapter.cpp new file mode 100644 index 000000000..9919ed844 --- /dev/null +++ b/unittest/tests/serialize/TestSerialBufferAdapter.cpp @@ -0,0 +1,143 @@ +#include + +#include +#include "../../core/CatchDefinitions.h" + + +static bool test_value_bool = true; +static uint16_t tv_uint16 {283}; +static std::array testArray; + +TEST_CASE("Serial Buffer Adapter", "[single-file]") { + size_t serialized_size = 0; + test_value_bool = true; + uint8_t * arrayPtr = testArray.data(); + std::array test_serial_buffer {5, 4, 3, 2, 1}; + SerialBufferAdapter tv_serial_buffer_adapter = + SerialBufferAdapter(test_serial_buffer.data(), + test_serial_buffer.size(), false); + tv_uint16 = 16; + + SECTION("Serialize without size field") { + SerializeAdapter::serialize(&test_value_bool, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &arrayPtr, &serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + + REQUIRE(serialized_size == 8); + REQUIRE(testArray[0] == true); + REQUIRE(testArray[1] == 5); + REQUIRE(testArray[2] == 4); + REQUIRE(testArray[3] == 3); + REQUIRE(testArray[4] == 2); + REQUIRE(testArray[5] == 1); + memcpy(&tv_uint16, testArray.data() + 6, sizeof(tv_uint16)); + REQUIRE(tv_uint16 == 16); + } + + SECTION("Serialize with size field") { + SerialBufferAdapter tv_serial_buffer_adapter_loc = + SerialBufferAdapter(test_serial_buffer.data(), + test_serial_buffer.size(), true); + serialized_size = 0; + arrayPtr = testArray.data(); + SerializeAdapter::serialize(&test_value_bool, &arrayPtr,&serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter_loc, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &arrayPtr, &serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + + REQUIRE(serialized_size == 9); + REQUIRE(testArray[0] == true); + REQUIRE(testArray[1] == 5); + REQUIRE(testArray[2] == 5); + REQUIRE(testArray[3] == 4); + REQUIRE(testArray[4] == 3); + REQUIRE(testArray[5] == 2); + REQUIRE(testArray[6] == 1); + memcpy(&tv_uint16, testArray.data() + 7, sizeof(tv_uint16)); + REQUIRE(tv_uint16 == 16); + } + + SECTION("Test set buffer function") { + SerialBufferAdapter tv_serial_buffer_adapter_loc = + SerialBufferAdapter((uint8_t*)nullptr, + 0, true); + tv_serial_buffer_adapter_loc.setBuffer(test_serial_buffer.data(), + test_serial_buffer.size()); + serialized_size = 0; + arrayPtr = testArray.data(); + SerializeAdapter::serialize(&test_value_bool, &arrayPtr,&serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter_loc, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &arrayPtr, &serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + REQUIRE(serialized_size == 9); + REQUIRE(testArray[0] == true); + REQUIRE(testArray[1] == 5); + REQUIRE(testArray[2] == 5); + REQUIRE(testArray[3] == 4); + REQUIRE(testArray[4] == 3); + REQUIRE(testArray[5] == 2); + REQUIRE(testArray[6] == 1); + memcpy(&tv_uint16, testArray.data() + 7, sizeof(tv_uint16)); + REQUIRE(tv_uint16 == 16); + } + + SECTION("Deserialization with size field") { + size_t buffer_size = 4; + memcpy(testArray.data(), &buffer_size, sizeof(uint16_t)); + testArray[2] = 1; + testArray[3] = 1; + testArray[4] = 1; + testArray[5] = 0; + std::array test_recv_array; + arrayPtr = testArray.data(); + // copy testArray[1] to testArray[4] into receive buffer, skip + // size field (testArray[0]) for deSerialization. + SerialBufferAdapter tv_serial_buffer_adapter3 = + SerialBufferAdapter(test_recv_array.data(), 4, true); + // Deserialization + size_t size = 6; + auto result = tv_serial_buffer_adapter3.deSerialize( + const_cast(&arrayPtr), &size, + SerializeIF::Endianness::MACHINE); + REQUIRE(result == retval::CATCH_OK); + CHECK(test_recv_array[0] == 1); + CHECK(test_recv_array[1] == 1); + CHECK(test_recv_array[2] == 1); + CHECK(test_recv_array[3] == 0); + } + + SECTION("Deserialization without size field") { + size_t buffer_size = 4; + memcpy(testArray.data(), &buffer_size, sizeof(uint16_t)); + testArray[2] = 1; + testArray[3] = 1; + testArray[4] = 1; + testArray[5] = 0; + std::array test_recv_array; + arrayPtr = testArray.data() + 2; + // copy testArray[1] to testArray[4] into receive buffer, skip + // size field (testArray[0]) + SerialBufferAdapter tv_serial_buffer_adapter3 = + SerialBufferAdapter(test_recv_array.data(), 4, false); + // Deserialization + size_t size = 4; + tv_serial_buffer_adapter3.deSerialize( + const_cast(&arrayPtr), &size, + SerializeIF::Endianness::MACHINE); + CHECK(test_recv_array[0] == 1); + CHECK(test_recv_array[1] == 1); + CHECK(test_recv_array[2] == 1); + CHECK(test_recv_array[3] == 0); + } +} diff --git a/unittest/tests/serialize/TestSerialLinkedPacket.cpp b/unittest/tests/serialize/TestSerialLinkedPacket.cpp new file mode 100644 index 000000000..0a09e430c --- /dev/null +++ b/unittest/tests/serialize/TestSerialLinkedPacket.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include "../../core/CatchDefinitions.h" +#include "TestSerialLinkedPacket.h" + + +TEST_CASE("Serial Linked Packet" , "[SerLinkPacket]") { + // perform set-up here + uint32_t header = 42; + std::array testArray {1,2,3}; + uint32_t tail = 96; + size_t packetMaxSize = 256; + uint8_t packet [packetMaxSize] = {}; + size_t packetLen = 0; + + SECTION("Test Deserialization with Serial Buffer Adapter.") { + // This is a serialization of a packet, made "manually". + // We generate a packet which store data big-endian by swapping some + // values. (like coming from ground). + header = EndianConverter::convertBigEndian(header); + std::memcpy(packet, &header, sizeof(header)); + packetLen += sizeof(header); + + std::copy(testArray.data(), testArray.data() + testArray.size(), + packet + packetLen); + packetLen += testArray.size(); + + tail = EndianConverter::convertBigEndian(tail); + std::memcpy(packet + packetLen, &tail, sizeof(tail)); + packetLen += sizeof(tail); + + //arrayprinter::print(packet, packetLen, OutputType::DEC); + + // This is the buffer which will be filled when testClass.deSerialize + // is called. + std::array bufferAdaptee = {}; + TestPacket testClass(packet, packetLen, bufferAdaptee.data(), + bufferAdaptee.size()); + const uint8_t* readOnlyPointer = packet; + // Deserialize big endian packet by setting bigEndian to true. + ReturnValue_t result = testClass.deSerialize(&readOnlyPointer, + &packetLen, SerializeIF::Endianness::BIG); + REQUIRE(result == retval::CATCH_OK); + CHECK(testClass.getHeader() == 42); + // Equivalent check. + // CHECK(testClass.getBuffer()[0] == 1); + CHECK(bufferAdaptee[0] == 1); + CHECK(bufferAdaptee[1] == 2); + CHECK(bufferAdaptee[2] == 3); + CHECK(testClass.getTail() == 96); + } + + SECTION("Test Serialization") { + // Same process as performed in setup, this time using the class + // instead of doing it manually. + TestPacket testClass(header, tail, testArray.data(), testArray.size()); + size_t serializedSize = 0; + uint8_t* packetPointer = packet; + // serialize for ground: bigEndian = true. + ReturnValue_t result = testClass.serialize(&packetPointer, + &serializedSize, packetMaxSize, SerializeIF::Endianness::BIG); + REQUIRE(result == retval::CATCH_OK); + // Result should be big endian now. + CHECK(packet[3] == 42); + CHECK(packet[4] == 1); + CHECK(packet[5] == 2); + CHECK(packet[6] == 3); + CHECK(packet[10] == 96); + } + + // perform tear-down here +} diff --git a/unittest/tests/serialize/TestSerialLinkedPacket.h b/unittest/tests/serialize/TestSerialLinkedPacket.h new file mode 100644 index 000000000..6c720577b --- /dev/null +++ b/unittest/tests/serialize/TestSerialLinkedPacket.h @@ -0,0 +1,61 @@ +#ifndef UNITTEST_HOSTED_TESTSERIALLINKEDPACKET_H_ +#define UNITTEST_HOSTED_TESTSERIALLINKEDPACKET_H_ + +#include +#include +#include +#include +#include + +class TestPacket: public SerialLinkedListAdapter { +public: + /** + * For Deserialization + */ + TestPacket(const uint8_t *somePacket, size_t size, uint8_t * storePointer, + size_t storeSize): + buffer(storePointer, storeSize) + { + setLinks(); + } + + /** + * For Serialization + */ + TestPacket(uint32_t header, uint32_t tail, + const uint8_t* parameters, size_t paramSize): + header(header), buffer(parameters, paramSize), + tail(tail) { + setLinks(); + } + + uint32_t getHeader() const { + return header.entry; + } + + const uint8_t * getBuffer() { + return buffer.entry.getConstBuffer(); + } + + const size_t getBufferLength() { + return buffer.getSerializedSize(); + } + + + uint16_t getTail() const { + return tail.entry; + } +private: + void setLinks() { + setStart(&header); + header.setNext(&buffer); + buffer.setNext(&tail); + tail.setEnd(); + } + + SerializeElement header = 0; + SerializeElement> buffer; + SerializeElement tail = 0; +}; + +#endif /* UNITTEST_TESTFW_NEWTESTS_TESTTEMPLATE_H_ */ diff --git a/unittest/tests/serialize/TestSerialization.cpp b/unittest/tests/serialize/TestSerialization.cpp new file mode 100644 index 000000000..4c9ba1818 --- /dev/null +++ b/unittest/tests/serialize/TestSerialization.cpp @@ -0,0 +1,129 @@ +#include + +#include "catch.hpp" +#include +#include "../../core/CatchDefinitions.h" + +static bool test_value_bool = true; +static uint8_t tv_uint8 {5}; +static uint16_t tv_uint16 {283}; +static uint32_t tv_uint32 {929221}; +static uint64_t tv_uint64 {2929329429}; + +static int8_t tv_int8 {-16}; +static int16_t tv_int16 {-829}; +static int32_t tv_int32 {-2312}; + +static float tv_float {8.2149214}; +static float tv_sfloat = {-922.2321321}; +static double tv_double {9.2132142141e8}; +static double tv_sdouble {-2.2421e19}; + +static std::array test_array; + +TEST_CASE( "Serialization size tests", "[TestSerialization]") { + //REQUIRE(unitTestClass.test_autoserialization() == 0); + REQUIRE(SerializeAdapter::getSerializedSize(&test_value_bool) == + sizeof(test_value_bool)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint8) == + sizeof(tv_uint8)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint16) == + sizeof(tv_uint16)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint32 ) == + sizeof(tv_uint32)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint64) == + sizeof(tv_uint64)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_int8) == + sizeof(tv_int8)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_int16) == + sizeof(tv_int16)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_int32) == + sizeof(tv_int32)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_float) == + sizeof(tv_float)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_sfloat) == + sizeof(tv_sfloat )); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_double) == + sizeof(tv_double)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_sdouble) == + sizeof(tv_sdouble)); +} + + +TEST_CASE("Auto Serialize Adapter testing", "[single-file]") { + size_t serialized_size = 0; + uint8_t * p_array = test_array.data(); + + SECTION("Serializing...") { + SerializeAdapter::serialize(&test_value_bool, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint8, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint32, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_int8, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_int16, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_int32, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint64, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_float, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_double, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_sfloat, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_sdouble, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + REQUIRE (serialized_size == 47); + } + + SECTION("Deserializing") { + p_array = test_array.data(); + size_t remaining_size = serialized_size; + SerializeAdapter::deSerialize(&test_value_bool, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint64, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_float, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_double, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sfloat, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sdouble, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + + REQUIRE(test_value_bool == true); + REQUIRE(tv_uint8 == 5); + REQUIRE(tv_uint16 == 283); + REQUIRE(tv_uint32 == 929221); + REQUIRE(tv_uint64 == 2929329429); + REQUIRE(tv_int8 == -16); + REQUIRE(tv_int16 == -829); + REQUIRE(tv_int32 == -2312); + + REQUIRE(tv_float == Approx(8.214921)); + REQUIRE(tv_double == Approx(9.2132142141e8)); + REQUIRE(tv_sfloat == Approx(-922.2321321)); + REQUIRE(tv_sdouble == Approx(-2.2421e19)); + } +} + + diff --git a/unittest/tests/storagemanager/TestNewAccessor.cpp b/unittest/tests/storagemanager/TestNewAccessor.cpp new file mode 100644 index 000000000..7bd0dee2f --- /dev/null +++ b/unittest/tests/storagemanager/TestNewAccessor.cpp @@ -0,0 +1,161 @@ +//#include +//#include +//#include "../../core/CatchDefinitions.h" +//#include +// +//TEST_CASE( "New Accessor" , "[NewAccessor]") { +// uint16_t numberOfElements[1] = {1}; +// uint16_t sizeofElements[1] = {10}; +// LocalPool<1> SimplePool = LocalPool<1>(0, sizeofElements, numberOfElements); +// std::array testDataArray; +// std::array receptionArray; +// store_address_t testStoreId; +// ReturnValue_t result = retval::CATCH_FAILED; +// +// for(size_t i = 0; i < testDataArray.size(); i++) { +// testDataArray[i] = i; +// } +// size_t size = 10; +// +// SECTION ("Simple tests getter functions") { +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// auto resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// resultPair.second.getDataCopy(receptionArray.data(), 20); +// CHECK(resultPair.second.getId() == testStoreId); +// CHECK(resultPair.second.size() == 10); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// std::copy(resultPair.second.data(), resultPair.second.data() + +// resultPair.second.size(), receptionArray.data()); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// { +// auto resultPairLoc = SimplePool.getData(testStoreId); +// REQUIRE(resultPairLoc.first == retval::CATCH_OK); +// // data should be deleted when accessor goes out of scope. +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == (int) StorageManagerIF::DATA_DOES_NOT_EXIST); +// +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// ConstStorageAccessor constAccessor(testStoreId); +// result = SimplePool.getData(testStoreId, constAccessor); +// REQUIRE(result == retval::CATCH_OK); +// constAccessor.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// // likewise, data should be deleted when accessor gets out of scope. +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == (int) StorageManagerIF::DATA_DOES_NOT_EXIST); +// +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// { +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// resultPair.second.release(); +// // now data should not be deleted anymore +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// resultPair.second.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// } +// +// +// SECTION("Simple tests modify functions") { +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// StorageAccessor accessor(testStoreId); +// result = SimplePool.modifyData(testStoreId, accessor); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(accessor.getId() == testStoreId); +// CHECK(accessor.size() == 10); +// accessor.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// std::copy(accessor.data(), accessor.data() + +// accessor.size(), receptionArray.data()); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// // data should be deleted when accessor goes out of scope +// } +// auto resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == (int) StorageManagerIF::DATA_DOES_NOT_EXIST); +// +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// auto resultPairLoc = SimplePool.modifyData(testStoreId); +// REQUIRE(resultPairLoc.first == retval::CATCH_OK); +// CHECK(resultPairLoc.second.getId() == testStoreId); +// CHECK(resultPairLoc.second.size() == 10); +// resultPairLoc.second.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// std::copy(resultPairLoc.second.data(), resultPairLoc.second.data() + +// resultPairLoc.second.size(), receptionArray.data()); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// resultPairLoc.second.release(); +// // data should not be deleted when accessor goes out of scope +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// } +// +// +// SECTION("Write tests") { +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// auto resultPair = SimplePool.modifyData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// testDataArray[9] = 42; +// resultPair.second.write(testDataArray.data(), 10, 0); +// // now data should not be deleted +// resultPair.second.release(); +// } +// auto resultConstPair = SimplePool.getData(testStoreId); +// REQUIRE(resultConstPair.first == retval::CATCH_OK); +// +// resultConstPair.second.getDataCopy(receptionArray.data(), 10); +// for(size_t i = 0; i < size-1; i++) { +// CHECK(receptionArray[i] == i ); +// } +// CHECK(receptionArray[9] == 42 ); +// +// auto resultPair = SimplePool.modifyData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// result = resultPair.second.write(testDataArray.data(), 20, 0); +// REQUIRE(result == retval::CATCH_FAILED); +// result = resultPair.second.write(testDataArray.data(), 10, 5); +// REQUIRE(result == retval::CATCH_FAILED); +// +// memset(testDataArray.data(), 42, 5); +// result = resultPair.second.write(testDataArray.data(), 5, 5); +// REQUIRE(result == retval::CATCH_OK); +// resultConstPair = SimplePool.getData(testStoreId); +// resultPair.second.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 5; i < 10; i++) { +// CHECK(receptionArray[i] == 42 ); +// } +// +// } +//} diff --git a/unittest/tests/storagemanager/TestPool.cpp b/unittest/tests/storagemanager/TestPool.cpp new file mode 100644 index 000000000..f278c40c7 --- /dev/null +++ b/unittest/tests/storagemanager/TestPool.cpp @@ -0,0 +1,296 @@ +//#include "CatchDefinitions.h" +// +//#include +//#include +//#include +// +//#include +//#include +// +//#include +// +// +//TEST_CASE( "Local Pool Simple Tests [1 Pool]" , "[TestPool]") { +//// uint16_t numberOfElements[1] = {1}; +//// uint16_t sizeofElements[1] = {10}; +// LocalPool::LocalPoolConfig config = {{1, 10}}; +// LocalPool simplePool(0, config); +// std::array testDataArray; +// std::array receptionArray; +// store_address_t testStoreId; +// ReturnValue_t result = retval::CATCH_FAILED; +// uint8_t *pointer = nullptr; +// const uint8_t * constPointer = nullptr; +// +// for(size_t i = 0; i < testDataArray.size(); i++) { +// testDataArray[i] = i; +// } +// size_t size = 10; +// +// SECTION ( "Basic tests") { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.getData(testStoreId, &constPointer, &size); +// REQUIRE(result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// memset(receptionArray.data(), 0, size); +// result = simplePool.modifyData(testStoreId, &pointer, &size); +// memcpy(receptionArray.data(), pointer, size); +// REQUIRE(result == retval::CATCH_OK); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// result = simplePool.deleteData(testStoreId); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.addData(&testStoreId, testDataArray.data(), 15); +// CHECK (result == (int) StorageManagerIF::DATA_TOO_LARGE); +// } +// +// SECTION ( "Reservation Tests ") { +// pointer = nullptr; +// result = simplePool.getFreeElement(&testStoreId, size, &pointer); +// REQUIRE (result == retval::CATCH_OK); +// memcpy(pointer, testDataArray.data(), size); +// constPointer = nullptr; +// result = simplePool.getData(testStoreId, &constPointer, &size); +// +// REQUIRE (result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// } +// +// SECTION ( "Add, delete, add, add when full") { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.getData(testStoreId, &constPointer, &size); +// REQUIRE( result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// result = simplePool.deleteData(testStoreId); +// REQUIRE(result == retval::CATCH_OK); +// +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.getData(testStoreId, &constPointer, &size); +// REQUIRE( result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// store_address_t newAddress; +// result = simplePool.addData(&newAddress, testDataArray.data(), size); +// REQUIRE(result == (int) StorageManagerIF::DATA_STORAGE_FULL); +// +// // Packet Index to high intentionally +// newAddress.packetIndex = 2; +// pointer = testDataArray.data(); +// result = simplePool.modifyData(newAddress, &pointer, &size); +// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); +// +// result = simplePool.deleteData(newAddress); +// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); +// +// newAddress.packetIndex = 0; +// newAddress.poolIndex = 2; +// result = simplePool.deleteData(newAddress); +// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); +// } +// +// SECTION ( "Initialize and clear store, delete with pointer") { +// result = simplePool.initialize(); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// simplePool.clearStore(); +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.modifyData(testStoreId, &pointer, &size); +// REQUIRE(result == retval::CATCH_OK); +// store_address_t newId; +// result = simplePool.deleteData(pointer, size, &testStoreId); +// REQUIRE(result == retval::CATCH_OK); +// REQUIRE(testStoreId.raw != (uint32_t) StorageManagerIF::INVALID_ADDRESS); +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// } +//} +// +//int runIdx = 0; +// +//TEST_CASE( "Local Pool Extended Tests [3 Pools]" , "[TestPool2]") { +// LocalPool::LocalPoolConfig* config; +// if(runIdx == 0) { +// config = new LocalPool::LocalPoolConfig{{10, 5}, {5, 10}, {2, 20}}; +// } +// else { +// // shufle the order, they should be sort implictely so that the +// // order is ascending for the page sizes. +// config = new LocalPool::LocalPoolConfig{{5, 10}, {2, 20}, {10, 5}}; +// size_t lastSize = 0; +// for(const auto& pair: *config) { +// CHECK(pair.second > lastSize); +// lastSize = pair.second; +// } +// } +// runIdx++; +// +// LocalPool simplePool(0, *config); +// std::array testDataArray; +// std::array receptionArray; +// store_address_t testStoreId; +// ReturnValue_t result = retval::CATCH_FAILED; +// for(size_t i = 0; i < testDataArray.size(); i++) { +// testDataArray[i] = i; +// } +// size_t size = 0; +// +// SECTION ("Basic tests") { +// size = 8; +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should be on second page of the pool now for 8 bytes +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == 0); +// +// size = 15; +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should be on third page of the pool now for 15 bytes +// CHECK(testStoreId.poolIndex == 2); +// CHECK(testStoreId.packetIndex == 0); +// +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should be on third page of the pool now for 15 bytes +// CHECK(testStoreId.poolIndex == 2); +// CHECK(testStoreId.packetIndex == 1); +// +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// // Should be on third page of the pool now for 15 bytes +// REQUIRE(result == (int) LocalPool::DATA_STORAGE_FULL); +// +// size = 8; +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should still work +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == 1); +// +// // fill the rest of the pool +// for(uint8_t idx = 2; idx < 5; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == idx); +// } +// } +// +// SECTION ("Fill Count and Clearing") { +// //SECTION("Basic tests"); +// uint8_t bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // fill count should be all zeros now. +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 0); +// CHECK(receptionArray[1] == 0); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 0); +// +// // now fill the store completely. +// size = 5; +// for(uint8_t idx = 0; idx < 10; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 0); +// CHECK(testStoreId.packetIndex == idx); +// } +// size = 10; +// for(uint8_t idx = 0; idx < 5; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == idx); +// } +// size = 20; +// for(uint8_t idx = 0; idx < 2; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 2); +// CHECK(testStoreId.packetIndex == idx); +// } +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // fill count should be all 100 now. +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 100); +// CHECK(receptionArray[1] == 100); +// CHECK(receptionArray[2] == 100); +// CHECK(receptionArray[3] == 100); +// +// // now clear the store +// simplePool.clearStore(); +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 0); +// CHECK(receptionArray[1] == 0); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 0); +// +// // now fill one page +// size = 5; +// for(uint8_t idx = 0; idx < 10; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 0); +// CHECK(testStoreId.packetIndex == idx); +// } +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // First page full, median fill count is 33 % +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 100); +// CHECK(receptionArray[1] == 0); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 33); +// +// // now fill second page +// size = 10; +// for(uint8_t idx = 0; idx < 5; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == idx); +// } +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // First and second page full, median fill count is 66 % +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 100); +// CHECK(receptionArray[1] == 100); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 66); +// +// // now clear first page +// simplePool.clearPage(0); +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // Second page full, median fill count is 33 % +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 0); +// CHECK(receptionArray[1] == 100); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 33); +// } +// +// delete(config); +//} diff --git a/unittest/tests/tests.mk b/unittest/tests/tests.mk new file mode 100644 index 000000000..47e634a26 --- /dev/null +++ b/unittest/tests/tests.mk @@ -0,0 +1,8 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/container/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/action/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/storagemanager/*.cpp) + +# OSAL not included for now. + +INCLUDES += $(CURRENTPATH) \ No newline at end of file diff --git a/unittest/testtemplate/TestTemplate.cpp b/unittest/testtemplate/TestTemplate.cpp new file mode 100644 index 000000000..6b5fc3d2a --- /dev/null +++ b/unittest/testtemplate/TestTemplate.cpp @@ -0,0 +1,31 @@ +#include +#include + + +/** + * @brief Template test file + * @details + * In each test case, the code outside the sections is executed + * for EACH section. + * The most common macros to perform tests are: + * - CHECK(...): assert expression and continues even if it fails + * - REQUIRE(...): test case fails if assertion fails + * + * Tests are generally sturctured in test cases and sections, see example + * below. + * + * More Documentation: + * - https://github.com/catchorg/Catch2 + * - https://github.com/catchorg/Catch2/blob/master/docs/assertions.md + * - https://github.com/catchorg/Catch2/blob/master/docs/test-cases-and-sections.md + */ +TEST_CASE("Dummy Test" , "[DummyTest]") { + uint8_t testVariable = 1; + //perform set-up here + CHECK(testVariable == 1); + SECTION("TestSection") { + // set-up is run for each section + REQUIRE(testVariable == 1); + } + // perform tear-down here +} diff --git a/unittest/unlockRealtime.sh b/unittest/unlockRealtime.sh new file mode 100644 index 000000000..b28d54904 --- /dev/null +++ b/unittest/unlockRealtime.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Run this script to unlock all permissions to run the linux binaries +# and create threads + +binaries=$(find $directory -type f -name "*.elf") + +echo Unlocking real time permissions for binaries and bash console... + +# Set up the soft realtime limit to maximum (99) +# Please note that the hard limit needs to be set to 99 too +# for this to work (check with ulimit -Hr). +# If that has not been done yet, add +# hard rtprio 99 +# to /etc/security/limits.conf +# It is also necessary and recommended to add +# soft rtprio 99 +# as well. This can also be done in the command line +# but would need to be done for each session. +ulimit -Sr 99 + +for binary in ${binaries}; do + sudo setcap 'cap_sys_nice=eip' ${binary} + result=$? + if [ ${result} = 0 ];then + echo ${binary} was unlocked + fi +done + +# sudo setcap 'cap_sys_nice=eip' /bin/bash +# result=$? +# if [ ${result} = 0 ];then +# echo /bin/bash was unlocked +# fi +