From b48a0a4a4ce5ea9b4e85b76223e357fe49ce6907 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 13 Apr 2020 22:45:23 +0200 Subject: [PATCH] unit test class continued. serialize adapter functions which are internal, extracted to separate class --- container/IsDerivedFrom.h | 9 ++ serialize/SerializeAdapter.h | 162 +++++++-------------------- serialize/SerializeAdapterInternal.h | 114 +++++++++++++++++++ test/UnitTestClass.cpp | 87 +++++++++++++- test/UnitTestClass.h | 18 +-- 5 files changed, 259 insertions(+), 131 deletions(-) create mode 100644 serialize/SerializeAdapterInternal.h diff --git a/container/IsDerivedFrom.h b/container/IsDerivedFrom.h index 520033db..8142a378 100644 --- a/container/IsDerivedFrom.h +++ b/container/IsDerivedFrom.h @@ -1,6 +1,13 @@ #ifndef ISDERIVEDFROM_H_ #define ISDERIVEDFROM_H_ +/** + * These template type checks are based on SFINAE + * (https://en.cppreference.com/w/cpp/language/sfinae) + * + * @tparam D Derived Type + * @tparam B Base Type + */ template class IsDerivedFrom { class No { @@ -9,7 +16,9 @@ class IsDerivedFrom { No no[3]; }; + // This will be chosen if B is the base type static Yes Test(B*); // declared, but not defined + // This will be chosen for anything else static No Test(... ); // declared, but not defined public: diff --git a/serialize/SerializeAdapter.h b/serialize/SerializeAdapter.h index fce8651b..d926bfce 100644 --- a/serialize/SerializeAdapter.h +++ b/serialize/SerializeAdapter.h @@ -1,19 +1,18 @@ #ifndef SERIALIZEADAPTER_H_ #define SERIALIZEADAPTER_H_ -#include #include -#include #include -#include +#include +#include /** - * @brief This adapter provides an interface to use the SerializeIF functions + * @brief These adapters provides an interface to use the SerializeIF functions * with arbitrary template objects to facilitate and simplify the * serialization of classes with different multiple different data types * into buffers and vice-versa. * @details - * Examples: + * * A report class is converted into a TM buffer. The report class implements a * serialize functions and calls the AutoSerializeAdapter::serialize function * repeatedly on all object data fields. The getSerializedSize function is @@ -26,137 +25,42 @@ * bigEndian specifies whether an endian swap is performed on the data before * serialization or deserialization. * - * If the target architecture is little endian (ARM), any data types created might - * have the wrong endiness if they are to be used for the FSFW. * There are three ways to retrieve data out of a buffer to be used in the FSFW - * to use regular aligned (big endian) data. - * This can also be applied to uint32_t and uint64_t: + * to use regular aligned (big endian) data. Examples: * * 1. Use the AutoSerializeAdapter::deSerialize function - * The pointer *buffer will be incremented automatically by the typeSize of the object, - * so this function can be called on &buffer repeatedly without adjusting pointer position. - * Set bigEndian parameter to true to perform endian swapping. - * + * The pointer *buffer will be incremented automatically by the typeSize + * of the object, so this function can be called on &buffer repeatedly + * without adjusting pointer position. Set bigEndian parameter to true + * to perform endian swapping, if necessary + * @code * uint16_t data; * int32_t dataLen = sizeof(data); - * ReturnValue_t result = AutoSerializeAdapter::deSerialize(&data,&buffer,&dataLen,true); - * - * 2. Perform a bitshift operation. Perform endian swapping if necessary: + * ReturnValue_t result = + * AutoSerializeAdapter::deSerialize(&data,&buffer,&dataLen,true); + * @endcode * + * 2. Perform a bitshift operation. Watch for for endianness: + * @code * uint16_t data; * data = buffer[targetByte1] << 8 | buffer[targetByte2]; - * data = EndianSwapper::swap(data); - * - * 3. Memcpy can be used when data is little-endian. Perform endian-swapping if necessary. + * data = EndianSwapper::swap(data); //optional, or swap order above + * @endcode * + * 3. memcpy or std::copy can also be used, but watch out if system + * endianness is different from required data endianness. + * Perform endian-swapping if necessary. + * @code * uint16_t data; * memcpy(&data,buffer + positionOfTargetByte1,sizeof(data)); - * data = EndianSwapper::swap(data); + * data = EndianSwapper::swap(data); //optional + * @endcode * * When serializing for downlink, the packets are generally serialized assuming * big endian data format like seen in TmPacketStored.cpp for example. * * @ingroup serialize */ -template -class SerializeAdapter_ { -public: - static ReturnValue_t serialize(const T* object, uint8_t** buffer, - size_t* size, const size_t max_size, bool bigEndian) { - size_t ignoredSize = 0; - if (size == NULL) { - size = &ignoredSize; - } - if (sizeof(T) + *size <= max_size) { - T tmp; - if (bigEndian) { - tmp = EndianSwapper::swap(*object); - } else { - tmp = *object; - } - memcpy(*buffer, &tmp, sizeof(T)); - *size += sizeof(T); - (*buffer) += sizeof(T); - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::BUFFER_TOO_SHORT; - } - } - - /** - * Deserialize buffer into object - * @param object [out] Object to be deserialized with buffer data - * @param buffer buffer containing the data. Non-Const pointer to non-const - * pointer to const buffer. - * @param size int32_t type to allow value to be values smaller than 0, - * needed for range/size checking - * @param bigEndian Specify endianness - * @return - */ - ReturnValue_t deSerialize(T* object, const uint8_t** buffer, ssize_t* size, - bool bigEndian) { - T tmp; - *size -= sizeof(T); - if (*size >= 0) { - memcpy(&tmp, *buffer, sizeof(T)); - if (bigEndian) { - *object = EndianSwapper::swap(tmp); - } else { - *object = tmp; - } - *buffer += sizeof(T); - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::STREAM_TOO_SHORT; - } - } - - size_t getSerializedSize(const T * object) { - return sizeof(T); - } - -}; - -template -class SerializeAdapter_ { -public: - ReturnValue_t serialize(const T* object, uint8_t** buffer, size_t* size, - const size_t max_size, bool bigEndian) const { - size_t ignoredSize = 0; - if (size == NULL) { - size = &ignoredSize; - } - return object->serialize(buffer, size, max_size, bigEndian); - } - size_t getSerializedSize(const T* object) const { - return object->getSerializedSize(); - } - - ReturnValue_t deSerialize(T* object, const uint8_t** buffer, ssize_t* size, - bool bigEndian) { - return object->deSerialize(buffer, size, bigEndian); - } -}; - -template -class SerializeAdapter { -public: - static ReturnValue_t serialize(const T* object, uint8_t** buffer, - size_t* size, const size_t max_size, bool bigEndian) { - SerializeAdapter_::Is> adapter; - return adapter.serialize(object, buffer, size, max_size, bigEndian); - } - static uint32_t getSerializedSize(const T* object) { - SerializeAdapter_::Is> adapter; - return adapter.getSerializedSize(object); - } - - static ReturnValue_t deSerialize(T* object, const uint8_t** buffer, - ssize_t* size, bool bigEndian) { - SerializeAdapter_::Is> adapter; - return adapter.deSerialize(object, buffer, size, bigEndian); - } -}; // No type specification necessary here. class AutoSerializeAdapter { @@ -180,4 +84,24 @@ public: } }; +template +class SerializeAdapter { +public: + static ReturnValue_t serialize(const T* object, uint8_t** buffer, + size_t* size, const size_t max_size, bool bigEndian) { + SerializeAdapter_::Is> adapter; + return adapter.serialize(object, buffer, size, max_size, bigEndian); + } + static uint32_t getSerializedSize(const T* object) { + SerializeAdapter_::Is> adapter; + return adapter.getSerializedSize(object); + } + + static ReturnValue_t deSerialize(T* object, const uint8_t** buffer, + ssize_t* size, bool bigEndian) { + SerializeAdapter_::Is> adapter; + return adapter.deSerialize(object, buffer, size, bigEndian); + } +}; + #endif /* SERIALIZEADAPTER_H_ */ diff --git a/serialize/SerializeAdapterInternal.h b/serialize/SerializeAdapterInternal.h new file mode 100644 index 00000000..46dbdf19 --- /dev/null +++ b/serialize/SerializeAdapterInternal.h @@ -0,0 +1,114 @@ +/** + * @file SerializeAdapterInternal.h + * + * @date 13.04.2020 + * @author R. Mueller + */ + +#ifndef FRAMEWORK_SERIALIZE_SERIALIZEADAPTERINTERNAL_H_ +#define FRAMEWORK_SERIALIZE_SERIALIZEADAPTERINTERNAL_H_ +#include +#include +#include + +/** + * This template specialization will be chosen for fundamental types. + * @tparam T + * @tparam + */ +template +class SerializeAdapter_ { +public: + /** + * + * @param object + * @param buffer + * @param size + * @param max_size + * @param bigEndian + * @return + */ + static ReturnValue_t serialize(const T* object, uint8_t** buffer, + size_t* size, const size_t max_size, bool bigEndian) { + size_t ignoredSize = 0; + if (size == nullptr) { + size = &ignoredSize; + } + if (sizeof(T) + *size <= max_size) { + T tmp; + if (bigEndian) { + tmp = EndianSwapper::swap(*object); + } else { + tmp = *object; + } + memcpy(*buffer, &tmp, sizeof(T)); + *size += sizeof(T); + (*buffer) += sizeof(T); + return HasReturnvaluesIF::RETURN_OK; + } else { + return SerializeIF::BUFFER_TOO_SHORT; + } + } + + /** + * Deserialize buffer into object + * @param object [out] Object to be deserialized with buffer data + * @param buffer buffer containing the data. Non-Const pointer to non-const + * pointer to const buffer. + * @param size int32_t type to allow value to be values smaller than 0, + * needed for range/size checking + * @param bigEndian Specify endianness + * @return + */ + ReturnValue_t deSerialize(T* object, const uint8_t** buffer, ssize_t* size, + bool bigEndian) { + T tmp; + *size -= sizeof(T); + if (*size >= 0) { + memcpy(&tmp, *buffer, sizeof(T)); + if (bigEndian) { + *object = EndianSwapper::swap(tmp); + } else { + *object = tmp; + } + *buffer += sizeof(T); + return HasReturnvaluesIF::RETURN_OK; + } else { + return SerializeIF::STREAM_TOO_SHORT; + } + } + + size_t getSerializedSize(const T * object) { + return sizeof(T); + } +}; + +/** + * This template specialization will be chosen for class derived from + * SerializeIF. + * @tparam T + * @tparam + */ +template +class SerializeAdapter_ { +public: + ReturnValue_t serialize(const T* object, uint8_t** buffer, size_t* size, + const size_t max_size, bool bigEndian) const { + size_t ignoredSize = 0; + if (size == NULL) { + size = &ignoredSize; + } + return object->serialize(buffer, size, max_size, bigEndian); + } + + size_t getSerializedSize(const T* object) const { + return object->getSerializedSize(); + } + + ReturnValue_t deSerialize(T* object, const uint8_t** buffer, ssize_t* size, + bool bigEndian) { + return object->deSerialize(buffer, size, bigEndian); + } +}; + +#endif /* FRAMEWORK_SERIALIZE_SERIALIZEADAPTERINTERNAL_H_ */ diff --git a/test/UnitTestClass.cpp b/test/UnitTestClass.cpp index 7050dece..ac570b21 100644 --- a/test/UnitTestClass.cpp +++ b/test/UnitTestClass.cpp @@ -17,7 +17,7 @@ UnitTestClass::UnitTestClass() { UnitTestClass::~UnitTestClass() { } -ReturnValue_t UnitTestClass::performTests() { +ReturnValue_t UnitTestClass::perform_tests() { ReturnValue_t result = test_serialization(); if(result != RETURN_OK) { return result; @@ -28,7 +28,11 @@ ReturnValue_t UnitTestClass::performTests() { ReturnValue_t UnitTestClass::test_serialization() { // Here, we test all serialization tools. First test basic cases. - ReturnValue_t result = test_autoserialization(); + ReturnValue_t result = test_endianness_tools(); + if(result != RETURN_OK) { + return result; + } + result = test_autoserialization(); if(result != RETURN_OK) { return result; } @@ -39,6 +43,47 @@ ReturnValue_t UnitTestClass::test_serialization() { return RETURN_OK; } +ReturnValue_t UnitTestClass::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(); + AutoSerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2, false); + // Little endian: Value one on first byte + if(test_array[0] != 1 and test_array[1] != 0) { + return put_error(TestIds::ENDIANNESS_TOOLS); + + } + p_array = test_array.data(); + size = 0; + AutoSerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2, true); + // Big endian: Value one on second byte + if(test_array[0] != 0 and test_array[1] != 1) { + return put_error(TestIds::ENDIANNESS_TOOLS); + } + + // Endianness paameter will be changed later. +// p_array = test_array.data(); +// ssize_t ssize = size; +// // Resulting parameter should be big endian +// AutoSerializeAdapter::deSerialize(&two_byte_value, +// const_cast(&p_array), &ssize, true); +// if(two_byte_value != 1) { +// return put_error(TestIds::ENDIANNESS_TOOLS); +// } +// +// ssize = size; +// p_array = test_array.data(); +// // Resulting parameter should be little endian +// AutoSerializeAdapter::deSerialize(&two_byte_value, +// const_cast(&p_array), &ssize, false); +// if(two_byte_value != 256) { +// return put_error(TestIds::ENDIANNESS_TOOLS); +// } + return RETURN_OK; +} + ReturnValue_t UnitTestClass::test_autoserialization() { current_id = TestIds::AUTO_SERIALIZATION_SIZE; // Unit Test getSerializedSize @@ -142,15 +187,21 @@ ReturnValue_t UnitTestClass::test_autoserialization() { // double tv_sdouble {-2.2421e19}; if(test_value_bool != true or tv_uint8 != 5 or tv_uint16 != 283 or tv_uint32 != 929221 or tv_uint64 != 2929329429 or tv_int8 != -16 or - tv_int16 != -829 or tv_int32 != -2312 or tv_float != 8.214921 or - tv_double != 9.2132142141e8 or tv_sfloat != -922.2321321 or - tv_sdouble != -2.2421e19) + tv_int16 != -829 or tv_int32 != -2312) { return put_error(current_id); } + + if(abs(tv_float - 8.214921) > 0.0001 or + abs(tv_double - 9.2132142141e8) > 0.01 or + abs(tv_sfloat - (-922.2321321)) > 0.0001 or + abs(tv_sdouble - (-2.2421e19)) > 0.01) { + return put_error(current_id); + } return RETURN_OK; } +// TODO: Also test for constant buffers. ReturnValue_t UnitTestClass::test_serial_buffer_adapter() { current_id = TestIds::SERIALIZATION_BUFFER_ADAPTER; @@ -203,6 +254,31 @@ ReturnValue_t UnitTestClass::test_serial_buffer_adapter() { { return put_error(current_id); } + memcpy(&tv_uint16, test_array.data() + 7, sizeof(tv_uint16)); + if(tv_uint16 != 16) { + return put_error(current_id); + } + + // Serialize with size field + SerialBufferAdapter tv_serial_buffer_adapter3 = + SerialBufferAdapter( + const_cast(test_serial_buffer.data()), + test_serial_buffer.size(), false); + serialized_size = 0; + p_array = test_array.data(); + AutoSerializeAdapter::serialize(&test_value_bool, &p_array,&serialized_size, + test_array.size(), false); + AutoSerializeAdapter::serialize(&tv_serial_buffer_adapter3, &p_array, + &serialized_size, test_array.size(), false); + AutoSerializeAdapter::serialize(&tv_uint16, &p_array, &serialized_size, + test_array.size(), false); + + 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 put_error(current_id); + } memcpy(&tv_uint16, test_array.data() + 6, sizeof(tv_uint16)); if(tv_uint16 != 16) { return put_error(current_id); @@ -215,3 +291,4 @@ ReturnValue_t UnitTestClass::put_error(TestIds current_id) { << static_cast(current_id) << "\r\n" << std::flush; return RETURN_FAILED; } + diff --git a/test/UnitTestClass.h b/test/UnitTestClass.h index c93ea0bc..078cf3c9 100644 --- a/test/UnitTestClass.h +++ b/test/UnitTestClass.h @@ -19,6 +19,8 @@ * 1. TMTC Services * 2. Serialization tools * 3. Framework internal algorithms + * + * TODO: Maybe use specialized framework. */ class UnitTestClass: public HasReturnvaluesIF { @@ -27,12 +29,13 @@ public: virtual~ UnitTestClass(); enum class TestIds { - AUTO_SERIALIZATION_SIZE = 0, - AUTO_SERIALIZATION_SERIALIZE = 1, - AUTO_SERIALIZATION_DESERIALIZE = 2, - SERIALIZATION_BUFFER_ADAPTER = 3, - SERIALIZATION_FIXED_ARRAY_LIST_ADAPTER = 4, - SERIALIZATION_COMBINATION = 5, + ENDIANNESS_TOOLS, + AUTO_SERIALIZATION_SIZE, + AUTO_SERIALIZATION_SERIALIZE, + AUTO_SERIALIZATION_DESERIALIZE , + SERIALIZATION_BUFFER_ADAPTER, + SERIALIZATION_FIXED_ARRAY_LIST_ADAPTER, + SERIALIZATION_COMBINATION, TMTC_SERVICES , MISC }; @@ -41,11 +44,12 @@ public: * Some function which calls all other tests * @return */ - ReturnValue_t performTests(); + ReturnValue_t perform_tests(); ReturnValue_t test_serialization(); ReturnValue_t test_autoserialization(); ReturnValue_t test_serial_buffer_adapter(); + ReturnValue_t test_endianness_tools(); private: uint32_t errorCounter = 0; TestIds current_id = TestIds::MISC;