diff --git a/globalfunctions/arrayprinter.cpp b/globalfunctions/arrayprinter.cpp index 25d6fcf4..7dc056b0 100644 --- a/globalfunctions/arrayprinter.cpp +++ b/globalfunctions/arrayprinter.cpp @@ -2,76 +2,124 @@ #include "../serviceinterface/ServiceInterface.h" #include + void arrayprinter::print(const uint8_t *data, size_t size, OutputType type, - bool printInfo, size_t maxCharPerLine) { + bool printInfo, size_t maxCharPerLine) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - if(printInfo) { - sif::info << "Printing data with size " << size << ": "; - } - sif::info << "["; + if(printInfo) { + sif::info << "Printing data with size " << size << ": " << std::endl; + } #else - sif::printInfo("Printing data with size %zu: [", size); -#endif - if(type == OutputType::HEX) { - arrayprinter::printHex(data, size, maxCharPerLine); - } - else if (type == OutputType::DEC) { - arrayprinter::printDec(data, size, maxCharPerLine); - } - else if(type == OutputType::BIN) { - arrayprinter::printBin(data, size); - } +#if FSFW_NO_C99_IO == 1 + sif::printInfo("Printing data with size %lu: \n", static_cast(size)); +#else + sif::printInfo("Printing data with size %zu: \n", size); +#endif /* FSFW_NO_C99_IO == 1 */ +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + + if(type == OutputType::HEX) { + arrayprinter::printHex(data, size, maxCharPerLine); + } + else if (type == OutputType::DEC) { + arrayprinter::printDec(data, size, maxCharPerLine); + } + else if(type == OutputType::BIN) { + arrayprinter::printBin(data, size); + } } void arrayprinter::printHex(const uint8_t *data, size_t size, - size_t maxCharPerLine) { + size_t maxCharPerLine) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::info << std::hex; - for(size_t i = 0; i < size; i++) { - sif::info << "0x" << static_cast(data[i]); - if(i < size - 1){ - sif::info << " , "; - if(i > 0 and i % maxCharPerLine == 0) { - sif::info << std::endl; + if(sif::info.crAdditionEnabled()) { + std::cout << "\r" << std::endl; + } - } - } - } - sif::info << std::dec; - sif::info << "]" << std::endl; + std::cout << "[" << std::hex; + for(size_t i = 0; i < size; i++) { + std::cout << "0x" << static_cast(data[i]); + if(i < size - 1) { + std::cout << " , "; + if(i > 0 and (i + 1) % maxCharPerLine == 0) { + std::cout << std::endl; + + } + } + } + std::cout << std::dec; + std::cout << "]" << std::endl; #else - // how much memory to reserve for printout? + // General format: 0x01, 0x02, 0x03 so it is number of chars times 6 + // plus line break plus small safety margin. + char printBuffer[(size + 1) * 7 + 1]; + size_t currentPos = 0; + for(size_t i = 0; i < size; i++) { + // To avoid buffer overflows. + if(sizeof(printBuffer) - currentPos <= 7) { + break; + } + + currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]); + if(i < size - 1) { + currentPos += sprintf(printBuffer + currentPos, ", "); + if(i > 0 and (i + 1) % maxCharPerLine == 0) { + currentPos += sprintf(printBuffer + currentPos, "\n"); + } + } + } + printf("[%s]\n", printBuffer); #endif } void arrayprinter::printDec(const uint8_t *data, size_t size, - size_t maxCharPerLine) { + size_t maxCharPerLine) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::info << std::dec; - for(size_t i = 0; i < size; i++) { - sif::info << static_cast(data[i]); - if(i < size - 1){ - sif::info << " , "; - if(i > 0 and i % maxCharPerLine == 0) { - sif::info << std::endl; - } - } - } - sif::info << "]" << std::endl; + if(sif::info.crAdditionEnabled()) { + std::cout << "\r" << std::endl; + } + + std::cout << "[" << std::dec; + for(size_t i = 0; i < size; i++) { + std::cout << static_cast(data[i]); + if(i < size - 1){ + std::cout << " , "; + if(i > 0 and (i + 1) % maxCharPerLine == 0) { + std::cout << std::endl; + } + } + } + std::cout << "]" << std::endl; #else - // how much memory to reserve for printout? + // General format: 32, 243, -12 so it is number of chars times 5 + // plus line break plus small safety margin. + char printBuffer[(size + 1) * 5 + 1]; + size_t currentPos = 0; + for(size_t i = 0; i < size; i++) { + // To avoid buffer overflows. + if(sizeof(printBuffer) - currentPos <= 5) { + break; + } + + currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]); + if(i < size - 1) { + currentPos += sprintf(printBuffer + currentPos, ", "); + if(i > 0 and (i + 1) % maxCharPerLine == 0) { + currentPos += sprintf(printBuffer + currentPos, "\n"); + } + } + } + printf("[%s]\n", printBuffer); #endif } void arrayprinter::printBin(const uint8_t *data, size_t size) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::info << "\n" << std::flush; for(size_t i = 0; i < size; i++) { - sif::info << "Byte " << i + 1 << ": 0b"<< - std::bitset<8>(data[i]) << ",\n" << std::flush; + sif::info << "Byte " << i + 1 << ": 0b" << std::bitset<8>(data[i]) << std::endl; } - sif::info << "]" << std::endl; #else - // how much memory to reserve for printout? + for(size_t i = 0; i < size; i++) { + sif::printInfo("Byte %d: 0b" BYTE_TO_BINARY_PATTERN "\n", i + 1, BYTE_TO_BINARY(data[i])); + } #endif } diff --git a/globalfunctions/arrayprinter.h b/globalfunctions/arrayprinter.h index e57d8e04..e6b3ea6b 100644 --- a/globalfunctions/arrayprinter.h +++ b/globalfunctions/arrayprinter.h @@ -1,20 +1,25 @@ #ifndef FRAMEWORK_GLOBALFUNCTIONS_ARRAYPRINTER_H_ #define FRAMEWORK_GLOBALFUNCTIONS_ARRAYPRINTER_H_ + #include #include enum class OutputType { - DEC, - HEX, - BIN + DEC, + HEX, + BIN }; namespace arrayprinter { + + + void print(const uint8_t* data, size_t size, OutputType type = OutputType::HEX, - bool printInfo = true, size_t maxCharPerLine = 12); -void printHex(const uint8_t* data, size_t size, size_t maxCharPerLine = 12); -void printDec(const uint8_t* data, size_t size, size_t maxCharPerLine = 12); + bool printInfo = true, size_t maxCharPerLine = 10); +void printHex(const uint8_t* data, size_t size, size_t maxCharPerLine = 10); +void printDec(const uint8_t* data, size_t size, size_t maxCharPerLine = 10); void printBin(const uint8_t* data, size_t size); + } #endif /* FRAMEWORK_GLOBALFUNCTIONS_ARRAYPRINTER_H_ */ diff --git a/osal/linux/PosixThread.cpp b/osal/linux/PosixThread.cpp index 2499b442..bd8e7258 100644 --- a/osal/linux/PosixThread.cpp +++ b/osal/linux/PosixThread.cpp @@ -152,7 +152,7 @@ void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) { " the requested " << stackMb << " MB" << std::endl; #else sif::printError("PosixThread::createTask: Insufficient memory for " - "the requested %zu MB\n", stackMb); + "the requested %lu MB\n", static_cast(stackMb)); #endif } else if(errno == EINVAL) { diff --git a/serviceinterface/ServiceInterfaceBuffer.cpp b/serviceinterface/ServiceInterfaceBuffer.cpp index 1a02d6e9..3f624564 100644 --- a/serviceinterface/ServiceInterfaceBuffer.cpp +++ b/serviceinterface/ServiceInterfaceBuffer.cpp @@ -167,7 +167,9 @@ std::string* ServiceInterfaceBuffer::getPreamble(size_t * preambleSize) { return &preamble; } - +bool ServiceInterfaceBuffer::crAdditionEnabled() const { + return addCrToPreamble; +} #ifdef UT699 #include "../osal/rtems/Interrupt.h" diff --git a/serviceinterface/ServiceInterfaceBuffer.h b/serviceinterface/ServiceInterfaceBuffer.h index b1a50848..9d3ce069 100644 --- a/serviceinterface/ServiceInterfaceBuffer.h +++ b/serviceinterface/ServiceInterfaceBuffer.h @@ -70,6 +70,8 @@ private: void putChars(char const* begin, char const* end); std::string* getPreamble(size_t * preambleSize = nullptr); + + bool crAdditionEnabled() const; }; #endif diff --git a/serviceinterface/ServiceInterfacePrinter.h b/serviceinterface/ServiceInterfacePrinter.h index fbdccdd1..6b062108 100644 --- a/serviceinterface/ServiceInterfacePrinter.h +++ b/serviceinterface/ServiceInterfacePrinter.h @@ -1,7 +1,24 @@ +#ifndef FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER +#define FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER + #if FSFW_DISABLE_PRINTOUT == 0 #include #endif +//! https://stackoverflow.com/questions/111928/is-there-a-printf-converter-to-print-in-binary-format +//! Can be used to print out binary numbers in human-readable format. +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') + + namespace sif { enum PrintLevel { @@ -38,3 +55,4 @@ void printError(const char* fmt, ...); } +#endif /* FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER */ diff --git a/serviceinterface/ServiceInterfaceStream.cpp b/serviceinterface/ServiceInterfaceStream.cpp index 59526536..ad14cd04 100644 --- a/serviceinterface/ServiceInterfaceStream.cpp +++ b/serviceinterface/ServiceInterfaceStream.cpp @@ -15,5 +15,9 @@ std::string* ServiceInterfaceStream::getPreamble() { return streambuf.getPreamble(); } +bool ServiceInterfaceStream::crAdditionEnabled() const { + return streambuf.crAdditionEnabled(); +} + #endif diff --git a/serviceinterface/ServiceInterfaceStream.h b/serviceinterface/ServiceInterfaceStream.h index f3cb2cd0..e3c0ea7a 100644 --- a/serviceinterface/ServiceInterfaceStream.h +++ b/serviceinterface/ServiceInterfaceStream.h @@ -39,6 +39,8 @@ public: */ std::string* getPreamble(); + bool crAdditionEnabled() const; + protected: ServiceInterfaceBuffer streambuf; }; diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 119ef243..24f8ef6f 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -1,2 +1,5 @@ add_subdirectory(internal) -add_subdirectory(tests) \ No newline at end of file + +if(LINK_CATCH2) + add_subdirectory(tests) +endif() \ No newline at end of file diff --git a/unittest/internal/CMakeLists.txt b/unittest/internal/CMakeLists.txt index 9c24317f..11fd2b2f 100644 --- a/unittest/internal/CMakeLists.txt +++ b/unittest/internal/CMakeLists.txt @@ -4,4 +4,5 @@ target_sources(${TARGET_NAME} PRIVATE ) add_subdirectory(osal) -add_subdirectory(serialize) \ No newline at end of file +add_subdirectory(serialize) +add_subdirectory(globalfunctions) \ No newline at end of file diff --git a/unittest/internal/InternalUnitTester.cpp b/unittest/internal/InternalUnitTester.cpp index bd41969c..a9394ad3 100644 --- a/unittest/internal/InternalUnitTester.cpp +++ b/unittest/internal/InternalUnitTester.cpp @@ -5,6 +5,7 @@ #include "osal/IntTestSemaphore.h" #include "osal/IntTestMutex.h" #include "serialize/IntTestSerialization.h" +#include "globalfunctions/TestArrayPrinter.h" #include @@ -12,17 +13,26 @@ InternalUnitTester::InternalUnitTester() {} InternalUnitTester::~InternalUnitTester() {} -ReturnValue_t InternalUnitTester::performTests() { +ReturnValue_t InternalUnitTester::performTests(struct InternalUnitTester::TestConfig& testConfig) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::info << "Running internal unit tests.." << std::endl; +#else + sif::printInfo("Running internal unit tests..\n"); #endif + testserialize::test_serialization(); testmq::testMq(); testsemaph::testBinSemaph(); testsemaph::testCountingSemaph(); testmutex::testMutex(); + if(testConfig.testArrayPrinter) { + arrayprinter::testArrayPrinter(); + } + #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::info << "Internal unit tests finished." << std::endl; +#else + sif::printInfo("Internal unit tests finished.\n"); #endif return RETURN_OK; } diff --git a/unittest/internal/InternalUnitTester.h b/unittest/internal/InternalUnitTester.h index d0b1c106..ae954c6a 100644 --- a/unittest/internal/InternalUnitTester.h +++ b/unittest/internal/InternalUnitTester.h @@ -4,6 +4,7 @@ #include "UnittDefinitions.h" #include "../../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. @@ -15,6 +16,10 @@ */ class InternalUnitTester: public HasReturnvaluesIF { public: + struct TestConfig { + bool testArrayPrinter; + }; + InternalUnitTester(); virtual~ InternalUnitTester(); @@ -22,7 +27,7 @@ public: * Some function which calls all other tests * @return */ - virtual ReturnValue_t performTests(); + virtual ReturnValue_t performTests(struct InternalUnitTester::TestConfig& testConfig); }; diff --git a/unittest/internal/UnittDefinitions.cpp b/unittest/internal/UnittDefinitions.cpp index 517f561a..ed4b59c1 100644 --- a/unittest/internal/UnittDefinitions.cpp +++ b/unittest/internal/UnittDefinitions.cpp @@ -5,7 +5,7 @@ sif::error << "Unit Tester error: Failed at test ID " << errorId << std::endl; #else - sif::printError("Unit Tester error: Failed at test ID 0x%08x", errorId); + sif::printError("Unit Tester error: Failed at test ID %s\n", errorId.c_str()); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ return HasReturnvaluesIF::RETURN_FAILED; } diff --git a/unittest/internal/globalfunctions/CMakeLists.txt b/unittest/internal/globalfunctions/CMakeLists.txt new file mode 100644 index 00000000..4ea49bf7 --- /dev/null +++ b/unittest/internal/globalfunctions/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${TARGET_NAME} PRIVATE + TestArrayPrinter.cpp +) diff --git a/unittest/internal/globalfunctions/TestArrayPrinter.cpp b/unittest/internal/globalfunctions/TestArrayPrinter.cpp new file mode 100644 index 00000000..de016d19 --- /dev/null +++ b/unittest/internal/globalfunctions/TestArrayPrinter.cpp @@ -0,0 +1,29 @@ +#include "TestArrayPrinter.h" + +void arrayprinter::testArrayPrinter() { + { + const std::array testDataSmall = {0x01, 0x02, 0x03, 0x04, 0x05}; + arrayprinter::print(testDataSmall.data(), testDataSmall.size()); + arrayprinter::print(testDataSmall.data(), testDataSmall.size(), OutputType::DEC); + arrayprinter::print(testDataSmall.data(), testDataSmall.size(), OutputType::BIN); + } + + { + std::array testDataMed; + for(size_t idx = 0; idx < testDataMed.size(); idx ++) { + testDataMed[idx] = testDataMed.size() - idx; + } + arrayprinter::print(testDataMed.data(), testDataMed.size()); + arrayprinter::print(testDataMed.data(), testDataMed.size(), OutputType::DEC, 8); + } + + { + std::array testDataLarge; + for(size_t idx = 0; idx < testDataLarge.size(); idx ++) { + testDataLarge[idx] = idx; + } + arrayprinter::print(testDataLarge.data(), testDataLarge.size()); + arrayprinter::print(testDataLarge.data(), testDataLarge.size(), OutputType::DEC); + } + +} diff --git a/unittest/internal/globalfunctions/TestArrayPrinter.h b/unittest/internal/globalfunctions/TestArrayPrinter.h new file mode 100644 index 00000000..50b82ca0 --- /dev/null +++ b/unittest/internal/globalfunctions/TestArrayPrinter.h @@ -0,0 +1,13 @@ +#ifndef FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_ +#define FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_ + +#include +#include + +namespace arrayprinter { + +void testArrayPrinter(); + +} + +#endif /* FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_ */ diff --git a/unittest/internal/internal.mk b/unittest/internal/internal.mk index 799fd796..1d4c9c99 100644 --- a/unittest/internal/internal.mk +++ b/unittest/internal/internal.mk @@ -1,3 +1,4 @@ CXXSRC += $(wildcard $(CURRENTPATH)/osal/*.cpp) CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/globalfunctions/*.cpp) CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp) \ No newline at end of file diff --git a/unittest/internal/osal/IntTestMutex.cpp b/unittest/internal/osal/IntTestMutex.cpp index 2a1584b8..13d87a8b 100644 --- a/unittest/internal/osal/IntTestMutex.cpp +++ b/unittest/internal/osal/IntTestMutex.cpp @@ -3,8 +3,8 @@ #include #include -#if defined(hosted) -#include +#if defined(WIN32) || defined(UNIX) +#include #include #include #endif @@ -19,10 +19,11 @@ void testmutex::testMutex() { } // timed_mutex from the C++ library specifies undefined behaviour if // the timed mutex is locked twice from the same thread. -#if defined(hosted) + // TODO: we should pass a define like FSFW_OSAL_HOST to the build. +#if defined(WIN32) || defined(UNIX) // This calls the function from // another thread and stores the returnvalue in a future. - auto future = std::async(&MutexIF::lockMutex, mutex, 1); + auto future = std::async(&MutexIF::lockMutex, mutex, MutexIF::TimeoutType::WAITING, 1); result = future.get(); #else result = mutex->lockMutex(MutexIF::TimeoutType::WAITING, 1); @@ -35,8 +36,12 @@ void testmutex::testMutex() { if(result != HasReturnvaluesIF::RETURN_OK) { unitt::put_error(id); } - result = mutex->unlockMutex(); + + // TODO: we should pass a define like FSFW_OSAL_HOST to the build. +#if !defined(WIN32) && !defined(UNIX) + result = mutex->unlockMutex(); if(result != MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX) { unitt::put_error(id); } +#endif } diff --git a/unittest/tests/globalfunctions/CMakeLists.txt b/unittest/tests/globalfunctions/CMakeLists.txt new file mode 100644 index 00000000..4ea49bf7 --- /dev/null +++ b/unittest/tests/globalfunctions/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${TARGET_NAME} PRIVATE + TestArrayPrinter.cpp +)