Merge branch 'master' into mueller/defaultcfg
This commit is contained in:
commit
0b855c5b55
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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) {
|
||||
|
@ -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_ */
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include "fwSubsystemIdRanges.h"
|
||||
//could be move to more suitable location
|
||||
#include <config/tmtc/subsystemIdRanges.h>
|
||||
#include <subsystemIdRanges.h>
|
||||
|
||||
typedef uint16_t EventId_t;
|
||||
typedef uint8_t EventSeverity_t;
|
||||
|
@ -2,7 +2,7 @@
|
||||
#define FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_
|
||||
|
||||
#include "FwClassIds.h"
|
||||
#include <config/returnvalues/classIds.h>
|
||||
#include <returnvalues/classIds.h>
|
||||
#include <cstdint>
|
||||
|
||||
#define MAKE_RETURN_CODE( number ) ((INTERFACE_ID << 8) + (number))
|
||||
|
49
unittest/README.md
Normal file
49
unittest/README.md
Normal file
@ -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).
|
||||
|
11
unittest/core/CatchDefinitions.cpp
Normal file
11
unittest/core/CatchDefinitions.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "CatchDefinitions.h"
|
||||
#include <fsfw/objectmanager/ObjectManagerIF.h>
|
||||
|
||||
StorageManagerIF* tglob::getIpcStoreHandle() {
|
||||
if(objectManager != nullptr) {
|
||||
return objectManager->get<StorageManagerIF>(objects::IPC_STORE);
|
||||
} else {
|
||||
sif::error << "Global object manager uninitialized" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
21
unittest/core/CatchDefinitions.h
Normal file
21
unittest/core/CatchDefinitions.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_
|
||||
#define FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_
|
||||
|
||||
#include <fsfw/ipc/messageQueueDefinitions.h>
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
#include <fsfw/storagemanager/StorageManagerIF.h>
|
||||
|
||||
namespace retval {
|
||||
static constexpr int CATCH_OK = static_cast<int>(HasReturnvaluesIF::RETURN_OK);
|
||||
static constexpr int CATCH_FAILED = static_cast<int>(HasReturnvaluesIF::RETURN_FAILED);
|
||||
}
|
||||
|
||||
namespace tconst {
|
||||
static constexpr MessageQueueId_t testQueueId = 42;
|
||||
}
|
||||
|
||||
namespace tglob {
|
||||
StorageManagerIF* getIpcStoreHandle();
|
||||
}
|
||||
|
||||
#endif /* FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ */
|
31
unittest/core/CatchRunner.cpp
Normal file
31
unittest/core/CatchRunner.cpp
Normal file
@ -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 <catch2/catch.hpp>
|
||||
|
||||
#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
|
42
unittest/core/CatchSetup.cpp
Normal file
42
unittest/core/CatchSetup.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "CatchDefinitions.h"
|
||||
|
||||
#include <testcfg/cdatapool/dataPoolInit.h>
|
||||
#include <testcfg/objects/Factory.h>
|
||||
|
||||
|
||||
#ifdef GCOV
|
||||
#include <gcov.h>
|
||||
#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;
|
||||
}
|
||||
|
3
unittest/core/core.mk
Normal file
3
unittest/core/core.mk
Normal file
@ -0,0 +1,3 @@
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp)
|
||||
|
||||
INCLUDES += $(CURRENTPATH)
|
10
unittest/core/printChar.cpp
Normal file
10
unittest/core/printChar.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include <fsfw/unittest/core/printChar.h>
|
||||
#include <cstdio>
|
||||
|
||||
void printChar(const char* character, bool errStream) {
|
||||
if(errStream) {
|
||||
std::putc(*character, stderr);
|
||||
return;
|
||||
}
|
||||
std::putc(*character, stdout);
|
||||
}
|
8
unittest/core/printChar.h
Normal file
8
unittest/core/printChar.h
Normal file
@ -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_ */
|
27
unittest/internal/InternalUnitTester.cpp
Normal file
27
unittest/internal/InternalUnitTester.cpp
Normal file
@ -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 <cstdlib>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
29
unittest/internal/InternalUnitTester.h
Normal file
29
unittest/internal/InternalUnitTester.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef FRAMEWORK_TEST_UNITTESTCLASS_H_
|
||||
#define FRAMEWORK_TEST_UNITTESTCLASS_H_
|
||||
|
||||
#include "UnittDefinitions.h"
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
|
||||
/**
|
||||
* @brief Can be used for internal testing, for example for hardware specific
|
||||
* tests which can not be run on a host-machine.
|
||||
*
|
||||
* TODO: A lot of ways to improve this class. A way for tests to subscribe
|
||||
* in this central class would be nice. Right now, this is the class
|
||||
* which simply calls all other tests from other files manually.
|
||||
* Maybe there is a better way..
|
||||
*/
|
||||
class InternalUnitTester: public HasReturnvaluesIF {
|
||||
public:
|
||||
InternalUnitTester();
|
||||
virtual~ InternalUnitTester();
|
||||
|
||||
/**
|
||||
* Some function which calls all other tests
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t performTests();
|
||||
};
|
||||
|
||||
|
||||
#endif /* FRAMEWORK_TEST_UNITTESTCLASS_H_ */
|
7
unittest/internal/UnittDefinitions.cpp
Normal file
7
unittest/internal/UnittDefinitions.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include <fsfw/unittest/internal/UnittDefinitions.h>
|
||||
|
||||
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;
|
||||
}
|
33
unittest/internal/UnittDefinitions.h
Normal file
33
unittest/internal/UnittDefinitions.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef UNITTEST_INTERNAL_UNITTDEFINITIONS_H_
|
||||
#define UNITTEST_INTERNAL_UNITTDEFINITIONS_H_
|
||||
|
||||
#include "../../returnvalues/HasReturnvaluesIF.h"
|
||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
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_ */
|
3
unittest/internal/internal.mk
Normal file
3
unittest/internal/internal.mk
Normal file
@ -0,0 +1,3 @@
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/osal/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp)
|
52
unittest/internal/osal/IntTestMq.cpp
Normal file
52
unittest/internal/osal/IntTestMq.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include <fsfw/ipc/MessageQueueIF.h>
|
||||
#include <fsfw/ipc/QueueFactory.h>
|
||||
#include <fsfw/unittest/internal/osal/IntTestMq.h>
|
||||
#include <fsfw/unittest/internal/UnittDefinitions.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
using retval = HasReturnvaluesIF;
|
||||
|
||||
void testmq::testMq() {
|
||||
std::string id = "[testMq]";
|
||||
MessageQueueIF* testSenderMq =
|
||||
QueueFactory::instance()->createMessageQueue(1);
|
||||
MessageQueueId_t testSenderMqId = testSenderMq->getId();
|
||||
|
||||
MessageQueueIF* testReceiverMq =
|
||||
QueueFactory::instance()->createMessageQueue(1);
|
||||
MessageQueueId_t testReceiverMqId = testReceiverMq->getId();
|
||||
std::array<uint8_t, 20> testData { 0 };
|
||||
testData[0] = 42;
|
||||
MessageQueueMessage testMessage(testData.data(), 1);
|
||||
testSenderMq->setDefaultDestination(testReceiverMqId);
|
||||
|
||||
|
||||
auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
|
||||
if(result != retval::RETURN_OK) {
|
||||
unitt::put_error(id);
|
||||
}
|
||||
MessageQueueMessage recvMessage;
|
||||
result = testReceiverMq->receiveMessage(&recvMessage);
|
||||
if(result != retval::RETURN_OK or recvMessage.getData()[0] != 42) {
|
||||
unitt::put_error(id);
|
||||
}
|
||||
|
||||
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
|
||||
if(result != retval::RETURN_OK) {
|
||||
unitt::put_error(id);
|
||||
}
|
||||
MessageQueueId_t senderId = 0;
|
||||
result = testReceiverMq->receiveMessage(&recvMessage,&senderId);
|
||||
if(result != retval::RETURN_OK or recvMessage.getData()[0] != 42) {
|
||||
unitt::put_error(id);
|
||||
}
|
||||
if(senderId != testSenderMqId) {
|
||||
unitt::put_error(id);
|
||||
}
|
||||
senderId = testReceiverMq->getLastPartner();
|
||||
if(senderId != testSenderMqId) {
|
||||
unitt::put_error(id);
|
||||
}
|
||||
|
||||
}
|
9
unittest/internal/osal/IntTestMq.h
Normal file
9
unittest/internal/osal/IntTestMq.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef UNITTEST_INTERNAL_INTESTMQ_H_
|
||||
#define UNITTEST_INTERNAL_INTESTMQ_H_
|
||||
|
||||
namespace testmq {
|
||||
void testMq();
|
||||
}
|
||||
|
||||
|
||||
#endif /* UNITTEST_INTERNAL_INTESTMQ_H_ */
|
42
unittest/internal/osal/IntTestMutex.cpp
Normal file
42
unittest/internal/osal/IntTestMutex.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "IntTestMutex.h"
|
||||
|
||||
#include <fsfw/ipc/MutexFactory.h>
|
||||
#include <unittest/internal/UnittDefinitions.h>
|
||||
|
||||
#if defined(hosted)
|
||||
#include <fsfw/osal/hosted/Mutex.h>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#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);
|
||||
}
|
||||
}
|
10
unittest/internal/osal/IntTestMutex.h
Normal file
10
unittest/internal/osal/IntTestMutex.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef UNITTEST_INTERNAL_INTTESTMUTEX_H_
|
||||
#define UNITTEST_INTERNAL_INTTESTMUTEX_H_
|
||||
|
||||
namespace testmutex {
|
||||
void testMutex();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* UNITTEST_INTERNAL_INTTESTMUTEX_H_ */
|
160
unittest/internal/osal/IntTestSemaphore.cpp
Normal file
160
unittest/internal/osal/IntTestSemaphore.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#include "IntTestSemaphore.h"
|
||||
#include <fsfw/tasks/SemaphoreFactory.h>
|
||||
#include <unittest/internal/UnittDefinitions.h>
|
||||
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
|
||||
#include <fsfw/timemanager/Stopwatch.h>
|
||||
|
||||
|
||||
void testsemaph::testBinSemaph() {
|
||||
std::string id = "[BinSemaphore]";
|
||||
SemaphoreIF* binSemaph =
|
||||
SemaphoreFactory::instance()->createBinarySemaphore();
|
||||
if(binSemaph == nullptr) {
|
||||
return;
|
||||
}
|
||||
testBinSemaphoreImplementation(binSemaph, id);
|
||||
SemaphoreFactory::instance()->deleteSemaphore(binSemaph);
|
||||
#if defined(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);
|
||||
}
|
||||
}
|
15
unittest/internal/osal/IntTestSemaphore.h
Normal file
15
unittest/internal/osal/IntTestSemaphore.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_
|
||||
#define UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_
|
||||
class SemaphoreIF;
|
||||
#include <string>
|
||||
|
||||
namespace testsemaph {
|
||||
void testBinSemaph();
|
||||
void testBinSemaphoreImplementation(SemaphoreIF* binSemaph, std::string id);
|
||||
void testCountingSemaph();
|
||||
void testCountingSemaphImplementation(SemaphoreIF* countingSemaph,
|
||||
std::string id);
|
||||
}
|
||||
|
||||
|
||||
#endif /* UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ */
|
230
unittest/internal/serialize/IntTestSerialization.cpp
Normal file
230
unittest/internal/serialize/IntTestSerialization.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
#include "IntTestSerialization.h"
|
||||
#include <fsfw/serialize/SerializeElement.h>
|
||||
#include <fsfw/serialize/SerialBufferAdapter.h>
|
||||
#include <unittest/internal/UnittDefinitions.h>
|
||||
#include <fsfw/serialize/SerializeIF.h>
|
||||
#include <array>
|
||||
|
||||
using retval = HasReturnvaluesIF;
|
||||
std::array<uint8_t, 512> testserialize::test_array = { 0 };
|
||||
|
||||
ReturnValue_t testserialize::test_serialization() {
|
||||
// Here, we test all serialization tools. First test basic cases.
|
||||
ReturnValue_t result = test_endianness_tools();
|
||||
if(result != retval::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
result = test_autoserialization();
|
||||
if(result != retval::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
result = test_serial_buffer_adapter();
|
||||
if(result != retval::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return retval::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t testserialize::test_endianness_tools() {
|
||||
std::string id = "[test_endianness_tools]";
|
||||
test_array[0] = 0;
|
||||
test_array[1] = 0;
|
||||
uint16_t two_byte_value = 1;
|
||||
size_t size = 0;
|
||||
uint8_t* p_array = test_array.data();
|
||||
SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2,
|
||||
SerializeIF::Endianness::MACHINE);
|
||||
// Little endian: Value one on first byte
|
||||
if(test_array[0] != 1 and test_array[1] != 0) {
|
||||
return unitt::put_error(id);
|
||||
|
||||
}
|
||||
|
||||
p_array = test_array.data();
|
||||
size = 0;
|
||||
SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2,
|
||||
SerializeIF::Endianness::BIG);
|
||||
// Big endian: Value one on second byte
|
||||
if(test_array[0] != 0 and test_array[1] != 1) {
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
return retval::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t testserialize::test_autoserialization() {
|
||||
std::string id = "[test_autoserialization]";
|
||||
// Unit Test getSerializedSize
|
||||
if(SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_bool) != sizeof(tv::tv_bool) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_uint8) != sizeof(tv::tv_uint8) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_uint16) != sizeof(tv::tv_uint16) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_uint32) != sizeof(tv::tv_uint32) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_uint64) != sizeof(tv::tv_uint64) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_int8) != sizeof(tv::tv_int8) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_double) != sizeof(tv::tv_double) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_int16) != sizeof(tv::tv_int16) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_int32) != sizeof(tv::tv_int32) or
|
||||
SerializeAdapter::
|
||||
getSerializedSize(&tv::tv_float) != sizeof(tv::tv_float))
|
||||
{
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
|
||||
size_t serialized_size = 0;
|
||||
uint8_t * p_array = test_array.data();
|
||||
|
||||
SerializeAdapter::serialize(&tv::tv_bool, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_uint8, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_uint16, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_uint32, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_int8, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_int16, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_int32, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_uint64, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_float, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_double, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_sfloat, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv::tv_sdouble, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
// expected size is 1 + 1 + 2 + 4 + 1 + 2 + 4 + 8 + 4 + 8 + 4 + 8
|
||||
if(serialized_size != 47) {
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
|
||||
p_array = test_array.data();
|
||||
size_t remaining_size = serialized_size;
|
||||
bool tv_bool;
|
||||
uint8_t tv_uint8;
|
||||
uint16_t tv_uint16;
|
||||
uint32_t tv_uint32;
|
||||
int8_t tv_int8;
|
||||
int16_t tv_int16;
|
||||
int32_t tv_int32;
|
||||
uint64_t tv_uint64;
|
||||
float tv_float;
|
||||
double tv_double;
|
||||
float tv_sfloat;
|
||||
double tv_sdouble;
|
||||
|
||||
SerializeAdapter::deSerialize(&tv_bool,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_uint8,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_uint16,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_uint32,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_int8,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_int16,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_int32,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_uint64,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_float,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_double,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_sfloat,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&tv_sdouble,
|
||||
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
|
||||
|
||||
if(tv_bool != tv::tv_bool or tv_uint8 != tv::tv_uint8 or
|
||||
tv_uint16 != tv::tv_uint16 or tv_uint32 != tv::tv_uint32 or
|
||||
tv_uint64 != tv::tv_uint64 or tv_int8 != tv::tv_int8 or
|
||||
tv_int16 != tv::tv_int16 or tv_int32 != tv::tv_int32)
|
||||
{
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
|
||||
// These epsilon values were just guessed.. It appears to work though.
|
||||
if(abs(tv_float - tv::tv_float) > 0.0001 or
|
||||
abs(tv_double - tv::tv_double) > 0.01 or
|
||||
abs(tv_sfloat - tv::tv_sfloat) > 0.0001 or
|
||||
abs(tv_sdouble - tv::tv_sdouble) > 0.01) {
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
|
||||
// Check overflow
|
||||
return retval::RETURN_OK;
|
||||
}
|
||||
|
||||
// TODO: Also test for constant buffers.
|
||||
ReturnValue_t testserialize::test_serial_buffer_adapter() {
|
||||
std::string id = "[test_serial_buffer_adapter]";
|
||||
|
||||
// I will skip endian swapper testing, its going to be changed anyway..
|
||||
// uint8_t tv::tv_uint8_swapped = EndianSwapper::swap(tv::tv_uint8);
|
||||
|
||||
size_t serialized_size = 0;
|
||||
uint8_t * p_array = test_array.data();
|
||||
std::array<uint8_t, 5> test_serial_buffer {5, 4, 3, 2, 1};
|
||||
SerialBufferAdapter<uint8_t> tv_serial_buffer_adapter =
|
||||
SerialBufferAdapter<uint8_t>(test_serial_buffer.data(),
|
||||
test_serial_buffer.size(), false);
|
||||
uint16_t testUint16 = 16;
|
||||
|
||||
SerializeAdapter::serialize(&tv::tv_bool, &p_array,&serialized_size,
|
||||
test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv_serial_buffer_adapter, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size,
|
||||
test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
|
||||
if(serialized_size != 8 or test_array[0] != true or test_array[1] != 5
|
||||
or test_array[2] != 4 or test_array[3] != 3 or test_array[4] != 2
|
||||
or test_array[5] != 1)
|
||||
{
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
memcpy(&testUint16, test_array.data() + 6, sizeof(testUint16));
|
||||
if(testUint16 != 16) {
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
|
||||
// Serialize with size field
|
||||
SerialBufferAdapter<uint8_t> tv_serial_buffer_adapter2 =
|
||||
SerialBufferAdapter<uint8_t>(test_serial_buffer.data(),
|
||||
test_serial_buffer.size(), true);
|
||||
serialized_size = 0;
|
||||
p_array = test_array.data();
|
||||
SerializeAdapter::serialize(&tv::tv_bool, &p_array,&serialized_size,
|
||||
test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&tv_serial_buffer_adapter2, &p_array,
|
||||
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size,
|
||||
test_array.size(), SerializeIF::Endianness::MACHINE);
|
||||
|
||||
if(serialized_size != 9 or test_array[0] != true or test_array[1] != 5
|
||||
or test_array[2] != 5 or test_array[3] != 4 or test_array[4] != 3
|
||||
or test_array[5] != 2 or test_array[6] != 1)
|
||||
{
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
memcpy(&testUint16, test_array.data() + 7, sizeof(testUint16));
|
||||
if(testUint16 != 16) {
|
||||
return unitt::put_error(id);
|
||||
}
|
||||
return retval::RETURN_OK;
|
||||
}
|
15
unittest/internal/serialize/IntTestSerialization.h
Normal file
15
unittest/internal/serialize/IntTestSerialization.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_
|
||||
#define UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
#include <array>
|
||||
|
||||
namespace testserialize {
|
||||
ReturnValue_t test_serialization();
|
||||
ReturnValue_t test_endianness_tools();
|
||||
ReturnValue_t test_autoserialization();
|
||||
ReturnValue_t test_serial_buffer_adapter();
|
||||
|
||||
extern std::array<uint8_t, 512> test_array;
|
||||
}
|
||||
|
||||
#endif /* UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */
|
3
unittest/lcov.sh
Normal file
3
unittest/lcov.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
lcov --capture --directory . --output-file coverage.info
|
||||
genhtml coverage.info --output-directory _coverage
|
46
unittest/testcfg/FSFWConfig.h
Normal file
46
unittest/testcfg/FSFWConfig.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef CONFIG_FSFWCONFIG_H_
|
||||
#define CONFIG_FSFWCONFIG_H_
|
||||
|
||||
#include <FSFWVersion.h>
|
||||
|
||||
//! 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 <translateObjects.h>
|
||||
#include <translateEvents.h>
|
||||
#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_ */
|
415
unittest/testcfg/Makefile-FSFW-Tests
Normal file
415
unittest/testcfg/Makefile-FSFW-Tests
Normal file
@ -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)
|
||||