Updating development with upstream changes #2

Closed
muellerr wants to merge 38 commits from mueller/master into development
79 changed files with 529 additions and 222 deletions

View File

@ -139,5 +139,4 @@ target_include_directories(${LIB_FSFW_NAME} PRIVATE
target_compile_options(${LIB_FSFW_NAME} PRIVATE
${WARNING_FLAGS}
${COMPILER_FLAGS}
${ABI_FLAGS}
)

View File

@ -9,9 +9,10 @@ PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
PoolDataSetBase::~PoolDataSetBase() {}
ReturnValue_t PoolDataSetBase::registerVariable(
PoolVariableIF *variable) {
if (state != States::DATA_SET_UNINITIALISED) {
if (state != States::STATE_SET_UNINITIALISED) {
sif::error << "DataSet::registerVariable: "
"Call made in wrong position." << std::endl;
return DataSetIF::DATA_SET_UNINITIALISED;
@ -33,15 +34,16 @@ ReturnValue_t PoolDataSetBase::registerVariable(
ReturnValue_t PoolDataSetBase::read(uint32_t lockTimeout) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
if (state == States::DATA_SET_UNINITIALISED) {
ReturnValue_t error = result;
if (state == States::STATE_SET_UNINITIALISED) {
lockDataPool(lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
result = readVariable(count);
if(result != RETURN_OK) {
break;
error = result;
}
}
state = States::DATA_SET_WAS_READ;
state = States::STATE_SET_WAS_READ;
unlockDataPool();
}
else {
@ -50,6 +52,10 @@ ReturnValue_t PoolDataSetBase::read(uint32_t lockTimeout) {
" member datasets!" << std::endl;
result = SET_WAS_ALREADY_READ;
}
if(error != HasReturnvaluesIF::RETURN_OK) {
result = error;
}
return result;
}
@ -71,7 +77,13 @@ ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER)
{
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->read(mutexTimeout);
}
else {
result = registeredVariables[count]->readWithoutLock();
}
if(result != HasReturnvaluesIF::RETURN_OK) {
result = INVALID_PARAMETER_DEFINITION;
}
@ -80,7 +92,7 @@ ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
}
ReturnValue_t PoolDataSetBase::commit(uint32_t lockTimeout) {
if (state == States::DATA_SET_WAS_READ) {
if (state == States::STATE_SET_WAS_READ) {
handleAlreadyReadDatasetCommit(lockTimeout);
return HasReturnvaluesIF::RETURN_OK;
}
@ -96,10 +108,15 @@ void PoolDataSetBase::handleAlreadyReadDatasetCommit(uint32_t lockTimeout) {
!= PoolVariableIF::VAR_READ
&& registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) {
registeredVariables[count]->commit(mutexTimeout);
}
else {
registeredVariables[count]->commitWithoutLock();
}
}
state = States::DATA_SET_UNINITIALISED;
}
state = States::STATE_SET_UNINITIALISED;
unlockDataPool();
}
@ -111,7 +128,13 @@ ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(uint32_t lockTimeout) {
== PoolVariableIF::VAR_WRITE
&& registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
registeredVariables[count]->commitWithoutLock();
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->commit(mutexTimeout);
}
else {
result = registeredVariables[count]->commitWithoutLock();
}
} else if (registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if (result != COMMITING_WITHOUT_READING) {
@ -121,7 +144,7 @@ ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(uint32_t lockTimeout) {
}
}
}
state = States::DATA_SET_UNINITIALISED;
state = States::STATE_SET_UNINITIALISED;
unlockDataPool();
return result;
}
@ -172,3 +195,9 @@ size_t PoolDataSetBase::getSerializedSize() const {
void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) {
this->registeredVariables = variablesContainer;
}
void PoolDataSetBase::setReadCommitProtectionBehaviour(
bool protectEveryReadCommit, uint32_t mutexTimeout) {
this->protectEveryReadCommitCall = protectEveryReadCommit;
this->mutexTimeout = mutexTimeout;
}

View File

@ -44,6 +44,7 @@ public:
/**
* @brief The read call initializes reading out all registered variables.
* It is mandatory to call commit after every read call!
* @details
* It iterates through the list of registered variables and calls all read()
* functions of the registered pool variables (which read out their values
@ -52,11 +53,12 @@ public:
* the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned.
*
* The data pool is locked during the whole read operation and
* freed afterwards.The state changes to "was written" after this operation.
* freed afterwards. It is mandatory to call commit after a read call,
* even if the read operation is not successful!
* @return
* - @c RETURN_OK if all variables were read successfully.
* - @c INVALID_PARAMETER_DEFINITION if PID, size or type of the
* requested variable is invalid.
* - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there
* is a type conflict.
* - @c SET_WAS_ALREADY_READ if read() is called twice without calling
* commit() in between
*/
@ -75,7 +77,7 @@ public:
* If the set does contain at least one variable which is not write-only
* commit() can only be called after read(). If the set only contains
* variables which are write only, commit() can be called without a
* preceding read() call.
* preceding read() call. Every read call must be followed by a commit call!
* @return - @c RETURN_OK if all variables were read successfully.
* - @c COMMITING_WITHOUT_READING if set was not read yet and
* contains non write-only variables
@ -89,6 +91,7 @@ public:
* @return
*/
virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override;
/**
* Provides the means to lock the underlying data structure to ensure
* thread-safety. Default implementation is empty
@ -114,6 +117,15 @@ public:
SerializeIF::Endianness streamEndianness) override;
protected:
/**
* Can be used to individually protect every read and commit call.
* @param protectEveryReadCommit
* @param mutexTimeout
*/
void setReadCommitProtectionBehaviour(bool protectEveryReadCommit,
uint32_t mutexTimeout = 20);
/**
* @brief The fill_count attribute ensures that the variables
* register in the correct array position and that the maximum
@ -124,14 +136,14 @@ protected:
* States of the seet.
*/
enum class States {
DATA_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED
DATA_SET_WAS_READ //!< DATA_SET_WAS_READ
STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED
STATE_SET_WAS_READ //!< DATA_SET_WAS_READ
};
/**
* @brief state manages the internal state of the data set,
* which is important e.g. for the behavior on destruction.
*/
States state = States::DATA_SET_UNINITIALISED;
States state = States::STATE_SET_UNINITIALISED;
/**
* @brief This array represents all pool variables registered in this set.
@ -144,6 +156,9 @@ protected:
void setContainer(PoolVariableIF** variablesContainer);
private:
bool protectEveryReadCommitCall = false;
uint32_t mutexTimeout = 20;
ReturnValue_t readVariable(uint16_t count);
void handleAlreadyReadDatasetCommit(uint32_t lockTimeout);
ReturnValue_t handleUnreadDatasetCommit(uint32_t lockTimeout);

View File

@ -1,19 +1,17 @@
#ifndef FSFW_DATAPOOL_POOLDATASETIF_H_
#define FSFW_DATAPOOL_POOLDATASETIF_H_
#include "ReadCommitIF.h"
#include "DataSetIF.h"
/**
* @brief Extendes the DataSetIF by adding abstract functions to lock
* and unlock a data pool and read/commit semantics.
*/
class PoolDataSetIF: public DataSetIF {
class PoolDataSetIF: public DataSetIF, public ReadCommitIF {
public:
virtual~ PoolDataSetIF() {};
virtual ReturnValue_t read(dur_millis_t lockTimeout) = 0;
virtual ReturnValue_t commit(dur_millis_t lockTimeout) = 0;
/**
* @brief Most underlying data structures will have a pool like structure
* and will require a lock and unlock mechanism to ensure

View File

@ -3,6 +3,7 @@
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../serialize/SerializeIF.h"
#include "ReadCommitIF.h"
/**
* @brief This interface is used to control data pool
@ -17,9 +18,9 @@
* @author Bastian Baetz
* @ingroup data_pool
*/
class PoolVariableIF : public SerializeIF {
class PoolVariableIF : public SerializeIF,
public ReadCommitIF {
friend class PoolDataSetBase;
friend class GlobDataSet;
friend class LocalPoolDataSetBase;
public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF;
@ -57,41 +58,6 @@ public:
*/
virtual void setValid(bool validity) = 0;
/**
* @brief The commit call shall write back a newly calculated local
* value to the data pool.
* @details
* It is assumed that these calls are implemented in a thread-safe manner!
*/
virtual ReturnValue_t commit(uint32_t lockTimeout) = 0;
/**
* @brief The read call shall read the value of this parameter from
* the data pool and store the content locally.
* @details
* It is assumbed that these calls are implemented in a thread-safe manner!
*/
virtual ReturnValue_t read(uint32_t lockTimeout) = 0;
protected:
/**
* @brief Same as commit with the difference that comitting will be
* performed without a lock
* @return
* This can be used if the lock protection is handled externally
* to avoid the overhead of locking and unlocking consecutively.
* Declared protected to avoid free public usage.
*/
virtual ReturnValue_t readWithoutLock() = 0;
/**
* @brief Same as commit with the difference that comitting will be
* performed without a lock
* @return
* This can be used if the lock protection is handled externally
* to avoid the overhead of locking and unlocking consecutively.
* Declared protected to avoid free public usage.
*/
virtual ReturnValue_t commitWithoutLock() = 0;
};
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;

31
datapool/ReadCommitIF.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef FSFW_DATAPOOL_READCOMMITIF_H_
#define FSFW_DATAPOOL_READCOMMITIF_H_
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
/**
* @brief Common interface for all software objects which employ read-commit
* semantics.
*/
class ReadCommitIF {
public:
virtual ~ReadCommitIF() {}
virtual ReturnValue_t read(uint32_t mutexTimeout) = 0;
virtual ReturnValue_t commit(uint32_t mutexTimeout) = 0;
protected:
//! Optional and protected because this is interesting for classes grouping
//! members with commit and read semantics where the lock is only necessary
//! once.
virtual ReturnValue_t readWithoutLock() {
return read(20);
}
virtual ReturnValue_t commitWithoutLock() {
return commit(20);
}
};
#endif /* FSFW_DATAPOOL_READCOMMITIF_H_ */

View File

@ -44,6 +44,9 @@ public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF;
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
virtual object_id_t getObjectId() const = 0;

View File

@ -577,7 +577,7 @@ ReturnValue_t LocalDataPoolManager::printPoolEntry(
if (poolIter == localPoolMap.end()) {
sif::debug << "HousekeepingManager::fechPoolEntry:"
<< " Pool entry not found." << std::endl;
return POOL_ENTRY_NOT_FOUND;
return HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND;
}
poolIter->second->print();
return HasReturnvaluesIF::RETURN_OK;

View File

@ -55,14 +55,11 @@ class LocalDataPoolManager {
public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER;
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
static constexpr ReturnValue_t QUEUE_OR_DESTINATION_NOT_SET = MAKE_RETURN_CODE(0x0);
static constexpr ReturnValue_t QUEUE_OR_DESTINATION_NOT_SET = MAKE_RETURN_CODE(0x02);
static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(0x03);
static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(0x04);
static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(0x05);
static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(0x01);
static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(0x02);
static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(0x03);
/**
* This constructor is used by a class which wants to implement
@ -380,14 +377,14 @@ ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId,
if (poolIter == localPoolMap.end()) {
sif::warning << "HousekeepingManager::fechPoolEntry: Pool entry "
"not found." << std::endl;
return POOL_ENTRY_NOT_FOUND;
return HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND;
}
*poolEntry = dynamic_cast< PoolEntry<T>* >(poolIter->second);
if(*poolEntry == nullptr) {
sif::debug << "HousekeepingManager::fetchPoolEntry:"
" Pool entry not found." << std::endl;
return POOL_ENTRY_TYPE_CONFLICT;
return HasLocalDataPoolIF::POOL_ENTRY_TYPE_CONFLICT;
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -8,7 +8,7 @@
LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
uint32_t setId, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables, bool noPeriodicHandling):
const size_t maxNumberOfVariables, bool periodicHandling):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
if(hkOwner == nullptr) {
// Configuration error.
@ -23,7 +23,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
mutex = MutexFactory::instance()->createMutex();
// Data creators get a periodic helper for periodic HK data generation.
if(not noPeriodicHandling) {
if(periodicHandling) {
periodicHelper = new PeriodicHousekeepingHelper(this);
}
}
@ -34,13 +34,9 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid,
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
sid.objectId);
if(hkOwner == nullptr) {
// Configuration error.
sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
<< "invalid!" << std::endl;
return;
}
if(hkOwner != nullptr) {
hkManager = hkOwner->getHkManagerHandle();
}
this->sid = sid;
mutex = MutexFactory::instance()->createMutex();
@ -50,8 +46,11 @@ LocalPoolDataSetBase::~LocalPoolDataSetBase() {
}
ReturnValue_t LocalPoolDataSetBase::lockDataPool(uint32_t timeoutMs) {
if(hkManager != nullptr) {
MutexIF* mutex = hkManager->getMutexHandle();
return mutex->lockMutex(MutexIF::TimeoutType::WAITING, timeoutMs);
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer,
@ -127,8 +126,11 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
}
ReturnValue_t LocalPoolDataSetBase::unlockDataPool() {
if(hkManager != nullptr) {
MutexIF* mutex = hkManager->getMutexHandle();
return mutex->unlockMutex();
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer,
@ -276,3 +278,9 @@ void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) {
}
this->valid = valid;
}
void LocalPoolDataSetBase::setReadCommitProtectionBehaviour(
bool protectEveryReadCommit, uint32_t mutexTimeout) {
PoolDataSetBase::setReadCommitProtectionBehaviour(protectEveryReadCommit,
mutexTimeout);
}

View File

@ -54,7 +54,7 @@ public:
*/
LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
uint32_t setId, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables, bool noPeriodicHandling = false);
const size_t maxNumberOfVariables, bool periodicHandling = true);
/**
* @brief Constructor for users of local pool data.
@ -77,6 +77,16 @@ public:
*/
~LocalPoolDataSetBase();
/**
* If the data is pulled from different local data pools, every read and
* commit call should be mutex protected for thread safety.
* This can be specified with the second parameter.
* @param dataCreator
* @param protectEveryReadCommit
*/
void setReadCommitProtectionBehaviour(bool protectEveryReadCommit,
uint32_t mutexTimeout = 20);
void setValidityBufferGeneration(bool withValidityBuffer);
sid_t getSid() const;
@ -128,6 +138,7 @@ public:
protected:
sid_t sid;
uint32_t mutexTimeout = 20;
MutexIF* mutex = nullptr;
bool diagnostic = false;
@ -181,7 +192,7 @@ protected:
*/
ReturnValue_t unlockDataPool() override;
LocalDataPoolManager* hkManager;
LocalDataPoolManager* hkManager = nullptr;
/**
* Set n-th bit of a byte, with n being the position from 0

View File

@ -11,14 +11,14 @@ inline LocalPoolVariable<T>::LocalPoolVariable(HasLocalDataPoolIF* hkOwner,
LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
template<typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId,
DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
inline LocalPoolVariable<T>::LocalPoolVariable(object_id_t poolOwner,
lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
template<typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId, DataSetIF *dataSet,
pool_rwm_t setReadWriteMode):
inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId,
DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId,
dataSet, setReadWriteMode){}
@ -40,11 +40,11 @@ inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
if(result != RETURN_OK and poolEntry != nullptr) {
if(result != RETURN_OK or poolEntry == nullptr) {
sif::error << "PoolVector: Read of local pool variable of object "
"0x" << std::hex << std::setw(8) << std::setfill('0') <<
hkManager->getOwner() << " and lp ID 0x" << localPoolId <<
std::dec << " failed.\n" << std::flush;
<< std::hex << std::setw(8) << std::setfill('0')
<< hkManager->getOwner() << " and lp ID " << localPoolId
<< std::dec << " failed." << std::setfill(' ') << std::endl;
return result;
}
this->value = *(poolEntry->address);
@ -62,7 +62,7 @@ inline ReturnValue_t LocalPoolVariable<T>::commit(dur_millis_t lockTimeout) {
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
if(readWriteMode == pool_rwm_t::VAR_READ) {
sif::debug << "LocalPoolVar: Invalid read write "
sif::debug << "LocalPoolVariable: Invalid read write "
"mode for commit() call." << std::endl;
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}

View File

@ -0,0 +1,41 @@
#ifndef FSFW_DATAPOOLLOCAL_POOLREADHELPER_H_
#define FSFW_DATAPOOLLOCAL_POOLREADHELPER_H_
#include <fsfw/datapoollocal/LocalPoolDataSetBase.h>
#include <FSFWConfig.h>
/**
* @brief Helper class to read data sets or pool variables
*/
class PoolReadHelper {
public:
PoolReadHelper(ReadCommitIF* readObject, uint32_t mutexTimeout = 20):
readObject(readObject), mutexTimeout(mutexTimeout) {
if(readObject != nullptr) {
readResult = readObject->read(mutexTimeout);
#if FSFW_PRINT_VERBOSITY_LEVEL == 1
sif::error << "PoolReadHelper: Read failed!" << std::endl;
#endif
}
}
ReturnValue_t getReadResult() const {
return readResult;
}
~PoolReadHelper() {
if(readObject != nullptr) {
readObject->commit(mutexTimeout);
}
}
private:
ReadCommitIF* readObject = nullptr;
ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK;
uint32_t mutexTimeout = 20;
};
#endif /* FSFW_DATAPOOLLOCAL_POOLREADHELPER_H_ */

View File

@ -3,11 +3,12 @@
#include "OBSWVersion.h"
#ifdef __cplusplus
#include "objects/systemObjectList.h"
#include "events/subsystemIdRanges.h"
#include "returnvalues/classIds.h"
#ifdef __cplusplus
namespace config {
#endif

View File

@ -13,9 +13,11 @@ target_sources(${LIB_FSFW_NAME}
)
if(UNIX)
find_package(Threads REQUIRED)
target_link_libraries(${LIB_FSFW_NAME}
PRIVATE
rt
pthread
${CMAKE_THREAD_LIBS_INIT}
)
endif()

View File

@ -18,8 +18,14 @@ target_sources(${LIB_FSFW_NAME}
Timer.cpp
)
target_link_libraries(${LIB_FSFW_NAME}
PRIVATE
find_package(Threads REQUIRED)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${CMAKE_THREAD_LIBS_INIT}
rt
pthread
)
target_link_libraries(${LIB_FSFW_NAME} INTERFACE
${CMAKE_THREAD_LIBS_INIT}
)

2
unittest/CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
add_subdirectory(internal)
add_subdirectory(tests)

View File

@ -1,59 +1,19 @@
## 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.
This folder contains testing and unit testing components.
### Instructions
To run the fsfw unittests in the project, perform following steps:
The easiest way to run the unittest contained in this folder is to follow
the steps in the [test repository](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests).
This is recommended even if the goal is to set up a custom test repository to have
a starting point.
1. Copy the testcfg folder the project root (folder containing the FSFW).
2. There is a makefile inside the testcfg folder which can be used to have
a starting point to compile the unit tests. Copy that Makefile to the project
root
3. Create a folder named catch2 (can have other name which requires Makefile
adaption) and copy the Catch2 header files there (NOTE: CMake support
not enabled yet!)
To set up a custom test repository or project, following steps can be performed:
### Eclipse CDT settings
1. Copy the user folder content into the project root.
2. Clone [Catch2](https://github.com/catchorg/Catch2) in the project root.
3. Use the `CMakeLists.txt` as a starting point to add tests and build the test
executable.
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,7 @@
target_sources(${TARGET_NAME} PRIVATE
InternalUnitTester.cpp
UnittDefinitions.cpp
)
add_subdirectory(osal)
add_subdirectory(serialize)

View File

@ -0,0 +1,5 @@
target_sources(${TARGET_NAME} PRIVATE
IntTestMq.cpp
IntTestMutex.cpp
IntTestSemaphore.cpp
)

View File

@ -1,8 +1,8 @@
#include "IntTestMq.h"
#include "../UnittDefinitions.h"
#include <fsfw/unittest/internal/UnittDefinitions.h>
#include "../../../ipc/MessageQueueIF.h"
#include "../../../ipc/QueueFactory.h"
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <array>

View File

@ -1,10 +1,10 @@
#include "IntTestMutex.h"
#include "../../../ipc/MutexFactory.h"
#include "../UnittDefinitions.h"
#include <fsfw/ipc/MutexFactory.h>
#include <fsfw/unittest/internal/UnittDefinitions.h>
#if defined(hosted)
#include "../../osal/hosted/Mutex.h"
#include <fsfw/osal/hosted/Mutex.h>
#include <thread>
#include <future>
#endif

View File

@ -1,10 +1,11 @@
#include "IntTestSemaphore.h"
#include "../UnittDefinitions.h"
#include <fsfw/unittest/internal/UnittDefinitions.h>
#include "../../../tasks/SemaphoreFactory.h"
#include "../../../serviceinterface/ServiceInterfaceStream.h"
#include "../../../timemanager/Stopwatch.h"
#include <fsfw/tasks/SemaphoreFactory.h>
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
#include <fsfw/timemanager/Stopwatch.h>
#include <cstdlib>
void testsemaph::testBinSemaph() {
std::string id = "[BinSemaphore]";
@ -138,7 +139,7 @@ void testsemaph::testCountingSemaphImplementation(SemaphoreIF* countingSemaph,
// 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) {
if(std::abs(static_cast<int32_t>(time - 10)) > 1) {
unitt::put_error(id);
}
}

View File

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

View File

@ -1,9 +1,9 @@
#include "IntTestSerialization.h"
#include "../UnittDefinitions.h"
#include <fsfw/unittest/internal/UnittDefinitions.h>
#include "../../../serialize/SerializeElement.h"
#include "../../../serialize/SerialBufferAdapter.h"
#include "../../../serialize/SerializeIF.h"
#include <fsfw/serialize/SerializeElement.h>
#include <fsfw/serialize/SerialBufferAdapter.h>
#include <fsfw/serialize/SerializeIF.h>
#include <array>

View File

@ -0,0 +1,6 @@
add_subdirectory(action)
add_subdirectory(container)
add_subdirectory(osal)
add_subdirectory(serialize)
add_subdirectory(storagemanager)

View File

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

View File

@ -1,8 +1,11 @@
#include "TestActionHelper.h"
#include <unittest/core/CatchDefinitions.h>
#include <fsfw/action/ActionHelper.h>
#include <fsfw/ipc/CommandMessage.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp>
TEST_CASE( "Action Helper" , "[ActionHelper]") {

View File

@ -3,7 +3,7 @@
#include <fsfw/action/HasActionsIF.h>
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/unittest/core/CatchDefinitions.h>
#include <unittest/core/CatchDefinitions.h>
#include <cstring>

View File

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

View File

@ -1,7 +1,7 @@
#include "../../core/CatchDefinitions.h"
#include "../../container/SimpleRingBuffer.h"
#include <unittest/core/CatchDefinitions.h>
#include <fsfw/container/SimpleRingBuffer.h>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <cstring>
TEST_CASE("Ring Buffer Test" , "[RingBufferTest]") {

View File

@ -1,8 +1,8 @@
#include "../../container/ArrayList.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include <fsfw/container/ArrayList.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
/**
* @brief Array List test

View File

@ -1,10 +1,9 @@
#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/FIFO.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include "../../container/DynamicFIFO.h"
#include "../../container/FIFO.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include <catch.hpp>
#include <CatchDefinitions.h>
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE( "Dynamic Fifo Tests", "[TestDynamicFifo]") {
INFO("Dynamic Fifo Tests");

View File

@ -1,10 +1,9 @@
#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/FIFO.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include "../../container/DynamicFIFO.h"
#include "../../container/FIFO.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE( "Static Fifo Tests", "[TestFifo]") {
INFO("Fifo Tests");

View File

@ -1,9 +1,8 @@
#include "../../core/CatchDefinitions.h"
#include <fsfw/container/FixedArrayList.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include "../../container/FixedArrayList.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE( "FixedArrayList Tests", "[TestFixedArrayList]") {

View File

@ -1,8 +1,8 @@
#include "../../container/FixedMap.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include <fsfw/container/FixedMap.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
template class FixedMap<unsigned int, unsigned short>;

View File

@ -1,8 +1,8 @@
#include "../../container/FixedOrderedMultimap.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include <fsfw/container/FixedOrderedMultimap.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE( "FixedOrderedMultimap Tests", "[TestFixedOrderedMultimap]") {
INFO("FixedOrderedMultimap Tests");

View File

@ -0,0 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE
TestMessageQueue.cpp
TestSemaphore.cpp
)

View File

@ -1,8 +1,10 @@
#include "../../ipc/MessageQueueIF.h"
#include "../../ipc/QueueFactory.h"
#include <catch.hpp>
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
#include <array>
#include "../../core/CatchDefinitions.h"
TEST_CASE("MessageQueue Basic Test","[TestMq]") {
MessageQueueIF* testSenderMq =

View File

@ -0,0 +1,5 @@
target_sources(${TARGET_NAME} PRIVATE
TestSerialBufferAdapter.cpp
TestSerialization.cpp
TestSerialLinkedPacket.cpp
)

View File

@ -1,7 +1,9 @@
#include "../../serialize/SerialBufferAdapter.h"
#include <fsfw/serialize/SerialBufferAdapter.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
#include <array>
static bool test_value_bool = true;

View File

@ -1,9 +1,12 @@
#include "TestSerialLinkedPacket.h"
#include "../../core/CatchDefinitions.h"
#include <unittest/core/CatchDefinitions.h>
#include "../../globalfunctions/arrayprinter.h"
#include <fsfw/globalfunctions/arrayprinter.h>
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
#include <array>
TEST_CASE("Serial Linked Packet" , "[SerLinkPacket]") {

View File

@ -37,7 +37,7 @@ public:
return buffer.entry.getConstBuffer();
}
const size_t getBufferLength() {
size_t getBufferLength() {
return buffer.getSerializedSize();
}

View File

@ -1,8 +1,10 @@
#include "../../serialize/SerializeAdapter.h"
#include <fsfw/serialize/SerializeAdapter.h>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <unittest/core/CatchDefinitions.h>
#include "catch.hpp"
#include <array>
#include "../../core/CatchDefinitions.h"
static bool test_value_bool = true;
static uint8_t tv_uint8 {5};
@ -119,10 +121,10 @@ TEST_CASE("Auto Serialize Adapter testing", "[single-file]") {
REQUIRE(tv_int16 == -829);
REQUIRE(tv_int32 == -2312);
REQUIRE(tv_float == Approx(8.214921));
REQUIRE(tv_double == Approx(9.2132142141e8));
REQUIRE(tv_sfloat == Approx(-922.2321321));
REQUIRE(tv_sdouble == Approx(-2.2421e19));
REQUIRE(tv_float == Catch::Approx(8.214921));
REQUIRE(tv_double == Catch::Approx(9.2132142141e8));
REQUIRE(tv_sfloat == Catch::Approx(-922.2321321));
REQUIRE(tv_sdouble == Catch::Approx(-2.2421e19));
}
}

View File

@ -0,0 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE
TestNewAccessor.cpp
TestPool.cpp
)

View File

@ -1,10 +1,8 @@
#include "CatchDefinitions.h"
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/storagemanager/LocalPool.h>
#include <catch.hpp>
#include <CatchDefinitions.h>
#include <catch2/catch_test_macros.hpp>
#include <unittest/core/CatchDefinitions.h>
#include <cstring>

View File

@ -0,0 +1,165 @@
################################################################################
# CMake support for the Flight Software Framework Tests
#
# Developed in an effort to replace Make with a modern build system.
#
# Author: R. Mueller
################################################################################
################################################################################
# Pre-Project preparation
################################################################################
cmake_minimum_required(VERSION 3.13)
# set(CMAKE_VERBOSE TRUE)
set(CMAKE_SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildsystem/cmake")
# Tests can be built with the Host OSAL or with the Linux OSAL.
if(NOT OS_FSFW)
set(OS_FSFW host CACHE STRING "OS for the FSFW.")
endif()
option(CUSTOM_UNITTEST_RUNNER
"Specify whether custom main or Catch2 main is used" TRUE
)
# Perform steps like loading toolchain files where applicable.
#include(${CMAKE_SCRIPT_PATH}/PreProjectConfig.cmake)
#pre_project_config()
# Project Name
project(fsfw_tests C CXX)
################################################################################
# Pre-Sources preparation
################################################################################
# Specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Set names and variables
set(TARGET_NAME ${CMAKE_PROJECT_NAME})
if(CUSTOM_UNITTEST_RUNNER)
set(CATCH2_TARGET Catch2)
else()
set(CATCH2_TARGET Catch2WithMain)
endif()
set(LIB_FSFW_NAME fsfw)
# Set path names
set(FSFW_PATH fsfw)
set(CATCH2_PATH Catch2)
set(FSFW_TESTS_PATH fsfw/unittest)
set(TEST_SETUP_PATH unittest)
# Analyse different OS and architecture/target options and
# determine BSP_PATH
# FreeRTOS
if(${OS_FSFW} STREQUAL linux)
add_definitions(-DUNIX -DLINUX)
find_package(Threads REQUIRED)
# Hosted
else()
if(WIN32)
add_definitions(-DWIN32)
elseif(UNIX)
find_package(Threads REQUIRED)
add_definitions(-DUNIX -DLINUX)
endif()
endif()
set(FSFW_CONFIG_PATH testcfg)
################################################################################
# Executable and Sources
################################################################################
# Add executable
add_executable(${TARGET_NAME})
# Add subdirectories
add_subdirectory(${FSFW_PATH})
add_subdirectory(${CATCH2_PATH})
add_subdirectory(${FSFW_CONFIG_PATH})
add_subdirectory(${FSFW_TESTS_PATH})
add_subdirectory(${TEST_SETUP_PATH})
################################################################################
# Post-Sources preparation
################################################################################
# Add libraries for all sources.
target_link_libraries(${TARGET_NAME} PRIVATE
${LIB_FSFW_NAME}
${CATCH2_TARGET}
)
# Add include paths for all sources.
target_include_directories(${TARGET_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${FSFW_CONFIG_PATH}
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(WARNING_FLAGS
-Wall
-Wextra
-Wshadow=local
-Wimplicit-fallthrough=1
-Wno-unused-parameter
-Wno-psabi
)
# Remove unused sections.
target_compile_options(${TARGET_NAME} PRIVATE
"-ffunction-sections"
"-fdata-sections"
)
# Removed unused sections.
target_link_options(${TARGET_NAME} PRIVATE
"-Wl,--gc-sections"
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(COMPILER_FLAGS "/permissive-")
endif()
if(CMAKE_VERBOSE)
message(STATUS "Warning flags: ${WARNING_FLAGS}")
endif()
# Compile options for all sources.
target_compile_options(${TARGET_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:C>:${WARNING_FLAGS} ${COMPILER_FLAGS}>
$<$<COMPILE_LANGUAGE:CXX>:${WARNING_FLAGS} ${COMPILER_FLAGS}>
${ABI_FLAGS}
)
if(NOT CMAKE_SIZE)
set(CMAKE_SIZE size)
if(WIN32)
set(FILE_SUFFIX ".exe")
endif()
endif()
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND echo "Build directory: ${CMAKE_BINARY_DIR}"
COMMAND echo "Target OSAL: ${OS_FSFW}"
COMMAND echo "Target Build Type: ${CMAKE_BUILD_TYPE}"
COMMAND ${CMAKE_SIZE} ${TARGET_NAME}${FILE_SUFFIX}
)
include (${CMAKE_CURRENT_SOURCE_DIR}/buildsystem/cmake/BuildType.cmake)
set_build_type()

View File

@ -0,0 +1,11 @@
target_sources(${TARGET_NAME}
PRIVATE
ipc/MissionMessageTypes.cpp
pollingsequence/PollingSequenceFactory.cpp
)
# Add include paths for the executable
target_include_directories(${TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -0,0 +1,7 @@
target_sources(${TARGET_NAME} PRIVATE
CatchDefinitions.cpp
CatchFactory.cpp
CatchRunner.cpp
CatchSetup.cpp
printChar.cpp
)