Merge branch 'master' into mueller/defaultcfg

This commit is contained in:
Steffen Gaisser 2020-10-29 20:09:31 +01:00
commit 0b855c5b55
72 changed files with 4162 additions and 61 deletions

0
.gitmodules vendored Normal file
View File

View 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;
}

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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) {

View File

@ -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_ */

View File

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

View File

@ -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
View 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).

View 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;
}
}

View File

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

View 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

View 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
View File

@ -0,0 +1,3 @@
CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp)
INCLUDES += $(CURRENTPATH)

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

View 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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View File

@ -0,0 +1,3 @@
CXXSRC += $(wildcard $(CURRENTPATH)/osal/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp)

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

View File

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

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

View File

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

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

View 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_ */

View 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;
}

View 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
View File

@ -0,0 +1,3 @@
#!/bin/bash
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory _coverage

View 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_ */

View 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)