add tests
This commit is contained in:
parent
9534e90ea7
commit
fc2158dfbc
@ -142,7 +142,7 @@ if(FSFW_BUILD_TESTS)
|
||||
configure_file(unittests/testcfg/TestsConfig.h.in tests/TestsConfig.h)
|
||||
|
||||
project(${FSFW_TEST_TGT} CXX C)
|
||||
add_executable(${FSFW_TEST_TGT})
|
||||
add_executable(${FSFW_TEST_TGT} unittests/datapool/testDataset.cpp)
|
||||
if(IPO_SUPPORTED AND FSFW_ENABLE_IPO)
|
||||
set_property(TARGET ${FSFW_TEST_TGT} PROPERTY INTERPROCEDURAL_OPTIMIZATION
|
||||
TRUE)
|
||||
|
@ -175,6 +175,19 @@ ReturnValue_t PoolDataSetBase::unlockDataPool() { return returnvalue::OK; }
|
||||
ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::serializeWithValidityBlob(
|
||||
uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
|
||||
uint8_t* validityPtr = nullptr;
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
@ -216,19 +229,6 @@ ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const s
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::serializeWithValidityBlob(
|
||||
uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
|
119
src/fsfw/housekeeping/Dataset.h
Normal file
119
src/fsfw/housekeeping/Dataset.h
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "SerializableWithValidityIF.h"
|
||||
#include "definitions.h"
|
||||
#include "fsfw/globalfunctions/bitutility.h"
|
||||
|
||||
namespace hk {
|
||||
class Dataset : public SerializeIF {
|
||||
public:
|
||||
explicit Dataset(dp::structure_id_t sid) : sid(sid) {}
|
||||
|
||||
[[nodiscard]] dp::structure_id_t getStructureId() const { return sid; }
|
||||
|
||||
void setAllChildrenValidity(bool valid) {
|
||||
for (auto &serializable : serializables) {
|
||||
serializable.get().setValid(valid);
|
||||
}
|
||||
}
|
||||
|
||||
void addSerializable(const std::reference_wrapper<SerializableWithValidityIF> serializable) {
|
||||
serializables.push_back(serializable);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getNumberOfSerializables() const { return serializables.size(); }
|
||||
|
||||
[[nodiscard]] ReturnValue_t serializeWithValidityBlob(uint8_t **buffer, size_t *size,
|
||||
size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
const uint8_t validityMaskSize = std::ceil(static_cast<float>(serializables.size()) / 8.0);
|
||||
uint8_t *validityPtr = nullptr;
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
|
||||
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
|
||||
// the initialization of a variable sized array
|
||||
std::vector<uint8_t> validityMask(validityMaskSize, 0);
|
||||
validityPtr = validityMask.data();
|
||||
#else
|
||||
uint8_t validityMask[validityMaskSize] = {};
|
||||
validityPtr = validityMask;
|
||||
#endif
|
||||
uint8_t validBufferIndex = 0;
|
||||
uint8_t validBufferIndexBit = 0;
|
||||
for (auto &serializable : serializables) {
|
||||
if (serializable.get().isValid()) {
|
||||
// Set bit at correct position
|
||||
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
|
||||
}
|
||||
if (validBufferIndexBit == 7) {
|
||||
validBufferIndex++;
|
||||
validBufferIndexBit = 0;
|
||||
} else {
|
||||
validBufferIndexBit++;
|
||||
}
|
||||
|
||||
result = serializable.get().serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size + validityMaskSize > maxSize) {
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
// copy validity buffer to end
|
||||
std::memcpy(*buffer, validityPtr, validityMaskSize);
|
||||
*size += validityMaskSize;
|
||||
*buffer += validityMaskSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
Endianness streamEndianness) const override {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
for (auto &serializable : serializables) {
|
||||
result = serializable.get().serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] ReturnValue_t serialize(uint8_t *buffer, size_t &serSize, size_t maxSize,
|
||||
Endianness streamEndianness) const override {
|
||||
return SerializeIF::serialize(buffer, serSize, maxSize, streamEndianness);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override {
|
||||
size_t size = 0;
|
||||
for (auto &serializable : serializables) {
|
||||
size += serializable.get().getSerializedSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
|
||||
const Endianness streamEndianness) override {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
for (auto &serializable : serializables) {
|
||||
result = serializable.get().deSerialize(buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
dp::structure_id_t sid;
|
||||
std::vector<std::reference_wrapper<SerializableWithValidityIF>> serializables;
|
||||
};
|
||||
|
||||
} // namespace hk
|
165
src/fsfw/housekeeping/DatasetElement.h
Normal file
165
src/fsfw/housekeeping/DatasetElement.h
Normal file
@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "Dataset.h"
|
||||
#include "SerializableWithValidityIF.h"
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
|
||||
namespace hk {
|
||||
|
||||
template <typename T>
|
||||
class ListVariable : public SerializableWithValidityIF {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit ListVariable(Dataset& list, Args... args) : entry(std::forward<Args>(args)...) {
|
||||
list.addSerializable(*this);
|
||||
}
|
||||
explicit ListVariable(Dataset& list) : entry() { list.addSerializable(*this); }
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override {
|
||||
return SerializeAdapter::serialize(&entry, buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override {
|
||||
return SerializeAdapter::getSerializedSize(&entry);
|
||||
}
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override {
|
||||
return SerializeAdapter::deSerialize(&entry, buffer, size, streamEndianness);
|
||||
}
|
||||
|
||||
explicit operator T() { return entry; }
|
||||
|
||||
ListVariable& operator=(T newValue) {
|
||||
entry = newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool isValid() const { return valid; };
|
||||
|
||||
void setValid(bool _valid) { this->valid = _valid; }
|
||||
|
||||
T* operator->() { return &entry; }
|
||||
|
||||
T get() const { return entry; }
|
||||
|
||||
bool valid = false;
|
||||
T entry{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using LVar = ListVariable<T>;
|
||||
|
||||
using lvar_u8 = LVar<uint8_t>;
|
||||
using lvar_u16 = LVar<uint16_t>;
|
||||
using lvar_u32 = LVar<uint32_t>;
|
||||
using lvar_u64 = LVar<uint64_t>;
|
||||
using lvar_i8 = LVar<int8_t>;
|
||||
using lvar_i16 = LVar<int16_t>;
|
||||
using lvar_i32 = LVar<int32_t>;
|
||||
using lvar_i64 = LVar<int64_t>;
|
||||
using lvar_f32 = LVar<float>;
|
||||
using lvar_f64 = LVar<double>;
|
||||
|
||||
template <typename T, size_t N>
|
||||
class ListVector : public SerializableWithValidityIF {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit ListVector(Dataset& list, Args... args) : entry(std::forward<Args>(args)...) {
|
||||
list.addSerializable(*this);
|
||||
}
|
||||
explicit ListVector(Dataset& list) : entry() { list.addSerializable(*this); }
|
||||
|
||||
const T* get() const { return entry; }
|
||||
T* getMut() { return entry; }
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
result = SerializeAdapter::serialize(entry + i, buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override {
|
||||
return SerializeAdapter::getSerializedSize(entry) * N;
|
||||
}
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
result = SerializeAdapter::deSerialize(entry + i, buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
explicit operator T() { return entry; }
|
||||
|
||||
ListVector& operator=(T newValue[N]) {
|
||||
entry = newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Array subscript operator for access
|
||||
T& operator[](size_t index) {
|
||||
// No exceptions, so user takes care of index validation..
|
||||
return entry[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const { return entry[index]; }
|
||||
|
||||
// Conversion operator
|
||||
explicit operator T*() { return entry; }
|
||||
explicit operator const T*() const { return entry; }
|
||||
|
||||
// Iterators for range-based for loops and STL compatibility
|
||||
T* begin() { return std::begin(entry); }
|
||||
T* end() { return std::end(entry); }
|
||||
const T* begin() const { return std::begin(entry); }
|
||||
const T* end() const { return std::end(entry); }
|
||||
const T* cbegin() const { return std::begin(entry); }
|
||||
const T* cend() const { return std::end(entry); }
|
||||
|
||||
[[nodiscard]] virtual bool isValid() const { return valid; };
|
||||
|
||||
void setValid(bool _valid) { this->valid = _valid; }
|
||||
|
||||
// Additional utility methods
|
||||
[[nodiscard]] constexpr size_t size() const { return N; }
|
||||
|
||||
bool valid = false;
|
||||
T entry[N];
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
using LVec = ListVector<T, N>;
|
||||
|
||||
template <size_t N>
|
||||
using lvec_u8 = LVec<uint8_t, N>;
|
||||
template <size_t N>
|
||||
using lvec_u16 = LVec<uint16_t, N>;
|
||||
template <size_t N>
|
||||
using lvec_u32 = LVec<uint32_t, N>;
|
||||
template <size_t N>
|
||||
using lvec_i8 = LVec<int8_t, N>;
|
||||
template <size_t N>
|
||||
using lvec_i16 = LVec<int16_t, N>;
|
||||
template <size_t N>
|
||||
using lvec_i32 = LVec<int32_t, N>;
|
||||
template <size_t N>
|
||||
using lvec_f32 = LVec<float, N>;
|
||||
template <size_t N>
|
||||
using lvec_f64 = LVec<double, N>;
|
||||
|
||||
} // namespace hk
|
10
src/fsfw/housekeeping/SerializableWithValidityIF.h
Normal file
10
src/fsfw/housekeeping/SerializableWithValidityIF.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "fsfw/serialize/SerializeIF.h"
|
||||
|
||||
class SerializableWithValidityIF : public SerializeIF {
|
||||
public:
|
||||
[[nodiscard]] virtual bool isValid() const = 0;
|
||||
|
||||
virtual void setValid(bool valid) = 0;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
target_sources(
|
||||
${FSFW_TEST_TGT} PRIVATE testLocalPoolVariable.cpp testLocalPoolVector.cpp
|
||||
testDataSet.cpp testPeriodicHkHelper.cpp)
|
||||
${FSFW_TEST_TGT}
|
||||
PRIVATE testLocalPoolVariable.cpp testLocalPoolVector.cpp testSharedSet.cpp
|
||||
testPeriodicHkHelper.cpp testDataset.cpp)
|
||||
|
82
unittests/datapool/testDataset.cpp
Normal file
82
unittests/datapool/testDataset.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include <fsfw/housekeeping/Dataset.h>
|
||||
#include <fsfw/housekeeping/DatasetElement.h>
|
||||
#include <fsfw/serialize/SerializeElement.h>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
constexpr auto TEST_ID = dp::structure_id_t(1, 2);
|
||||
|
||||
class TestDatasetSmall : public hk::Dataset {
|
||||
public:
|
||||
TestDatasetSmall() : hk::Dataset(TEST_ID) {}
|
||||
|
||||
hk::lvar_u8 test0{*this};
|
||||
hk::lvar_u32 test1{*this};
|
||||
};
|
||||
|
||||
class TestDatasetLarger : public hk::Dataset {
|
||||
public:
|
||||
TestDatasetLarger() : hk::Dataset(TEST_ID) {}
|
||||
|
||||
hk::lvar_u8 test0{*this};
|
||||
hk::lvar_u32 test1{*this};
|
||||
hk::lvar_u16 test2{*this};
|
||||
hk::lvar_i32 test3{*this};
|
||||
hk::lvar_f32 test4{*this};
|
||||
hk::lvar_f64 test5{*this};
|
||||
hk::lvar_u8 test6{*this};
|
||||
hk::lvar_i16 test7{*this};
|
||||
hk::lvec_u16<2> test8{*this};
|
||||
};
|
||||
|
||||
TEST_CASE("Pool Dataset Test", "[datapool]") {
|
||||
TestDatasetSmall dataset;
|
||||
CHECK(dataset.getStructureId() == TEST_ID);
|
||||
CHECK(!dataset.test0.isValid());
|
||||
dataset.test0.setValid(true);
|
||||
CHECK(dataset.test0.isValid());
|
||||
|
||||
SECTION("Pool Dataset Serialization Test 1") {
|
||||
uint8_t buf[64]{};
|
||||
dataset.test0 = 55;
|
||||
dataset.test1 = 502392;
|
||||
size_t serLen = 0;
|
||||
CHECK(dataset.serialize(buf, serLen, sizeof(buf), SerializeIF::Endianness::NETWORK) ==
|
||||
returnvalue::OK);
|
||||
CHECK(buf[0] == 55);
|
||||
CHECK(serLen == 5);
|
||||
uint32_t readBack = 0;
|
||||
size_t dummy = 0;
|
||||
CHECK(SerializeAdapter::deSerialize(&readBack, buf + 1, &dummy,
|
||||
SerializeIF::Endianness::NETWORK) == returnvalue::OK);
|
||||
CHECK(readBack == 502392);
|
||||
CHECK(buf[5] == 0);
|
||||
}
|
||||
|
||||
SECTION("Pool Dataset Serialization With Validity") {
|
||||
uint8_t buf[64]{};
|
||||
dataset.test0 = 55;
|
||||
dataset.test1 = 502392;
|
||||
dataset.test0.setValid(true);
|
||||
dataset.test1.setValid(true);
|
||||
size_t serLen = 0;
|
||||
uint8_t* dataPtr = buf;
|
||||
CHECK(dataset.serializeWithValidityBlob(&dataPtr, &serLen, sizeof(buf),
|
||||
SerializeIF::Endianness::NETWORK) == returnvalue::OK);
|
||||
CHECK(buf[5] == 0b11000000);
|
||||
}
|
||||
|
||||
SECTION("Larger Pool Dataset Serialization With Validity") {
|
||||
uint8_t buf[64]{};
|
||||
TestDatasetLarger datasetLarge;
|
||||
datasetLarge.setAllChildrenValidity(true);
|
||||
size_t serLen = 0;
|
||||
uint8_t* dataPtr = buf;
|
||||
CHECK(datasetLarge.serializeWithValidityBlob(
|
||||
&dataPtr, &serLen, sizeof(buf), SerializeIF::Endianness::NETWORK) == returnvalue::OK);
|
||||
CHECK(serLen == 32);
|
||||
CHECK(buf[30] == 0b11111111);
|
||||
CHECK(buf[31] == 0b10000000);
|
||||
CHECK(buf[32] == 0);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
using namespace returnvalue;
|
||||
using namespace lpool;
|
||||
|
||||
TEST_CASE("DataSetTest", "[DataSetTest]") {
|
||||
TEST_CASE("DataSetTest", "[datapool]") {
|
||||
auto queue = MessageQueueMock(1, MessageQueueIF::NO_QUEUE);
|
||||
TestPoolOwner poolOwner(queue, objects::TEST_LOCAL_POOL_OWNER_BASE);
|
||||
poolOwner.initialize();
|
||||
@ -57,17 +57,23 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
|
||||
PoolReadGuard readHelper(&localSet);
|
||||
REQUIRE(readHelper.getReadResult() == returnvalue::OK);
|
||||
CHECK(localSet.localPoolVarUint8.value == 0);
|
||||
CHECK(!localSet.localPoolVarUint8.isValid());
|
||||
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
|
||||
CHECK(!localSet.localPoolVarFloat.isValid());
|
||||
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
|
||||
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
|
||||
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
|
||||
CHECK(!localSet.localPoolUint16Vec.isValid());
|
||||
|
||||
// Now set new values, commit should be done by read helper automatically
|
||||
localSet.localPoolVarUint8 = 232;
|
||||
localSet.localPoolVarUint8.setValid(true);
|
||||
localSet.localPoolVarFloat = -2324.322;
|
||||
localSet.localPoolVarFloat.setValid(true);
|
||||
localSet.localPoolUint16Vec.value[0] = 232;
|
||||
localSet.localPoolUint16Vec.value[1] = 23923;
|
||||
localSet.localPoolUint16Vec.value[2] = 1;
|
||||
localSet.localPoolUint16Vec.setValid(true);
|
||||
}
|
||||
|
||||
// Zero out some values for next test
|
||||
@ -85,10 +91,13 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
|
||||
PoolReadGuard readHelper(&localSet);
|
||||
REQUIRE(readHelper.getReadResult() == returnvalue::OK);
|
||||
CHECK(localSet.localPoolVarUint8.value == 232);
|
||||
CHECK(localSet.localPoolVarUint8.isValid());
|
||||
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(-2324.322));
|
||||
CHECK(localSet.localPoolVarFloat.isValid());
|
||||
CHECK(localSet.localPoolUint16Vec.value[0] == 232);
|
||||
CHECK(localSet.localPoolUint16Vec.value[1] == 23923);
|
||||
CHECK(localSet.localPoolUint16Vec.value[2] == 1);
|
||||
CHECK(localSet.localPoolUint16Vec.isValid());
|
||||
|
||||
// Now we serialize these values into a buffer without the validity buffer
|
||||
maxSize = localSet.getSerializedSize();
|
||||
@ -145,4 +154,8 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
|
||||
CHECK(sharedSet.commit() == returnvalue::OK);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Serialize with Validity Blob") {
|
||||
// TODO: Write test.
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
|
||||
#include <fsfw/housekeeping/PeriodicHkHelper.h>
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
|
||||
#include "poolDefinitions.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user