diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dbf4b8cc446417fe950e09b20534c433b34a0a5..2bed5291f564a84555fd204b4101274dd17a03b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,10 +31,11 @@ add_library(common OBJECT ) # Specify the .cpp files for the executables +file(GLOB x86_main_SRC "src/Platform/x86/*.cpp") add_executable(ecss_services src/main.cpp $<TARGET_OBJECTS:common> - src/Platform/x86/Service.cpp + ${x86_main_SRC} ) IF (EXISTS "${PROJECT_SOURCE_DIR}/lib/Catch2/CMakeLists.txt") diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index 2f3c067eea091d025221e350ea9726eb39a9d354..a55a0cae7bee5cf177294d65921684785632c844 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -15,20 +15,13 @@ class Message; class ErrorHandler { private: /** - * Log the error to a logging facility. Currently, this just displays the error on the screen. - * - * @todo This function MUST be moved as platform-dependent code. Currently, it uses g++ specific - * functions for desktop. + * Log the error to a logging facility. Platform-dependent. */ template<typename ErrorType> static void logError(const Message &message, ErrorType errorType); /** - * Log an error without a Message to a logging facility. Currently, this just displays the error - * on the screen. - * - * @todo This function MUST be moved as platform-dependent code. Currently, it uses g++ specific - * functions for desktop. + * Log an error without a Message to a logging facility. Platform-dependent. */ template<typename ErrorType> static void logError(ErrorType errorType); @@ -213,6 +206,35 @@ public: reportError(message, errorCode); } } + + /** + * Convert a parameter given in C++ to an ErrorSource that can be easily used in comparisons. + * @tparam ErrorType One of the enums specified in ErrorHandler. + * @param error An error code of a specific type + * @return The corresponding ErrorSource + */ + template<typename ErrorType> + inline static ErrorSource findErrorSource(ErrorType error) { + // While this may seem like a "hacky" way to convert enums to ErrorSource, it should be + // optimised by the compiler to constant time. + + if (typeid(ErrorType) == typeid(AcceptanceErrorType)) { + return Acceptance; + } + if (typeid(ErrorType) == typeid(ExecutionStartErrorType)) { + return ExecutionStart; + } + if (typeid(ErrorType) == typeid(ExecutionProgressErrorType)) { + return ExecutionProgress; + } + if (typeid(ErrorType) == typeid(ExecutionCompletionErrorType)) { + return ExecutionCompletion; + } + if (typeid(ErrorType) == typeid(RoutingErrorType)) { + return Routing; + } + return Internal; + } }; #endif //PROJECT_ERRORHANDLER_HPP diff --git a/src/ErrorHandler.cpp b/src/ErrorHandler.cpp index a8a71de9318270908590eddbb3def5873ddb6abe..b0266ebd7762e9a1b6155f60dffb2016ee23027d 100644 --- a/src/ErrorHandler.cpp +++ b/src/ErrorHandler.cpp @@ -41,28 +41,5 @@ void ErrorHandler::reportError(const Message &message, RoutingErrorType errorCod } void ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType errorCode) { - logError(errorCode); -} - -template<typename ErrorType> -void ErrorHandler::logError(const Message &message, ErrorType errorType) { - std::cerr - /* - * Gets the error class name from the template - * Note: This is g++-dependent code and should only be used for debugging. - */ - << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) - << " Error " << "[" << static_cast<uint16_t>(message.serviceType) << "," << - static_cast<uint16_t>(message.messageType) << "]: " << errorType << std::endl; -} - -template<typename ErrorType> -void ErrorHandler::logError(ErrorType errorType) { - std::cerr - /* - * Gets the error class name from the template - * Note: This is g++-dependent code and should only be used for debugging. - */ - << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) - << " Error: " << errorType << std::endl; + logError(UnknownInternalError); } diff --git a/src/Platform/x86/ErrorHandler.cpp b/src/Platform/x86/ErrorHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6119440cc09527624c64a4adc517e7d6c551e7ee --- /dev/null +++ b/src/Platform/x86/ErrorHandler.cpp @@ -0,0 +1,40 @@ +/** + * This file specifies the logging utilities for x86 desktop platforms. These logging functions + * just print the error to screen (via stderr). + */ + +#include <iostream> +#include <cxxabi.h> +#include <ErrorHandler.hpp> +#include <Message.hpp> + +// TODO: Find a way to reduce the number of copies of this chunk +template void ErrorHandler::logError(const Message &, ErrorHandler::AcceptanceErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::ExecutionStartErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::ExecutionProgressErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::ExecutionCompletionErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::RoutingErrorType); +template void ErrorHandler::logError(ErrorHandler::InternalErrorType); + +template<typename ErrorType> +void ErrorHandler::logError(const Message &message, ErrorType errorType) { + std::cerr + /* + * Gets the error class name from the template + * Note: This is g++-dependent code and should only be used for debugging. + */ + << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) + << " Error " << "[" << static_cast<uint16_t>(message.serviceType) << "," << + static_cast<uint16_t>(message.messageType) << "]: " << errorType << std::endl; +} + +template<typename ErrorType> +void ErrorHandler::logError(ErrorType errorType) { + std::cerr + /* + * Gets the error class name from the template + * Note: This is g++-dependent code and should only be used for debugging. + */ + << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) + << " Error: " << errorType << std::endl; +} diff --git a/test/Services/ServiceTests.hpp b/test/Services/ServiceTests.hpp index 740cc9ff05511d4d75b0c6ad56baa1fae0e11240..0c9f5b922501c10d7f6c7b28ebed119c9246e2ba 100644 --- a/test/Services/ServiceTests.hpp +++ b/test/Services/ServiceTests.hpp @@ -2,6 +2,7 @@ #define ECSS_SERVICES_TESTS_SERVICES_SERVICETESTS_HPP #include <vector> +#include <map> #include <Message.hpp> #include <ServicePool.hpp> @@ -11,8 +12,24 @@ * @todo See if members of this class can be made non-static */ class ServiceTests { + /** + * The list of Messages that have been sent as a result of all the processing. + */ static std::vector<Message> queuedMessages; + /** + * The list of Errors that the ErrorHandler caught + * @var A multimap with keys (ErrorSource, ErrorType) and values of 1 + * @todo If errors get more complex, this should hold the complete error information + */ + static std::multimap<std::pair<ErrorHandler::ErrorSource, uint16_t>, bool> thrownErrors; + + /** + * Whether an error assertion function was called, indicating that we are expecting to see + * Errors thrown after this Message + */ + static bool expectingErrors; + public: /** * Get a message from the list of queued messages to send @@ -29,6 +46,17 @@ public: queuedMessages.push_back(message); } + /** + * Add one error to the list of occurred errors. + * + * @param errorSource The source of the error. + * @param errorCode The integer code of the error, coming directly from one of the ErrorCode + * enumerations in ErrorHandler. + */ + static void addError(ErrorHandler::ErrorSource errorSource, uint16_t errorCode) { + thrownErrors.emplace(std::make_pair(errorSource, errorCode), 1); + } + /** * Counts the number of messages in the queue */ @@ -48,9 +76,53 @@ public: */ static void reset() { queuedMessages.clear(); + thrownErrors.clear(); + expectingErrors = false; Services.reset(); } + +/** + * Return whether an error assertion function was called, which means that we are expecting this + * request to contain errors + * @return + */ + static bool isExpectingErrors() { + return expectingErrors; + } + + /** + * Find if there are *no* thrown errors + * @return True if 0 errors were thrown after the message + * @todo Implement a way to run this assertion at the end of every test + */ + static bool hasNoErrors() { + return thrownErrors.empty(); + } + + /** + * Find the number of thrown errors after the processing of this Message. + */ + static uint64_t countErrors() { + expectingErrors = true; + + return thrownErrors.size(); + } + + /** + * Find if an error + * @tparam ErrorType An enumeration of ErrorHandler + * @param errorType The error code of the Error, corresponding to the correct type as + * specified in ErrorHandler + */ + template<typename ErrorType> + static bool thrownError(ErrorType errorType) { + ErrorHandler::ErrorSource errorSource = ErrorHandler::findErrorSource(errorType); + + expectingErrors = true; + + return thrownErrors.find(std::make_pair(errorSource, errorType)) != thrownErrors.end(); + } }; #endif //ECSS_SERVICES_TESTS_SERVICES_SERVICETESTS_HPP diff --git a/test/TestPlatform.cpp b/test/TestPlatform.cpp index 10f9cdf366fd4950138248a6564b33c7d8dd0a10..0880d64ea865d220846ac5659068606cbce65d62 100644 --- a/test/TestPlatform.cpp +++ b/test/TestPlatform.cpp @@ -5,15 +5,49 @@ #include <Service.hpp> #include "Services/ServiceTests.hpp" +// Explicit template specializations for the logError() function +template void ErrorHandler::logError(const Message &, ErrorHandler::AcceptanceErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::ExecutionStartErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::ExecutionProgressErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::ExecutionCompletionErrorType); +template void ErrorHandler::logError(const Message &, ErrorHandler::RoutingErrorType); +template void ErrorHandler::logError(ErrorHandler::InternalErrorType); + +// Initialisation of ServiceTests properties std::vector<Message> ServiceTests::queuedMessages = std::vector<Message>(); +std::multimap<std::pair<ErrorHandler::ErrorSource, uint16_t>, bool> ServiceTests::thrownErrors = + std::multimap<std::pair<ErrorHandler::ErrorSource, uint16_t>, bool>(); +bool ServiceTests::expectingErrors = false; void Service::storeMessage(const Message &message) { + // Just add the message to the queue ServiceTests::queue(message); } +template<typename ErrorType> +void ErrorHandler::logError(const Message &message, ErrorType errorType) { + logError(errorType); +} + +template<typename ErrorType> +void ErrorHandler::logError(ErrorType errorType) { + ServiceTests::addError(ErrorHandler::findErrorSource(errorType), errorType); +} + struct ServiceTestsListener : Catch::TestEventListenerBase { using TestEventListenerBase::TestEventListenerBase; // inherit constructor + void sectionEnded(Catch::SectionStats const §ionStats) override { + // Make sure we don't have any errors + if (not ServiceTests::isExpectingErrors()) { + // An Error was thrown with this Message. If you expected this to happen, please call a + // corresponding assertion function from ServiceTests to silence this message. + + //TODO: Uncomment the following line as soon as Issue #19 is closed + // CHECK(ServiceTests::hasNoErrors()); + } + } + void testCaseEnded(Catch::TestCaseStats const &testCaseStats) override { // Tear-down after a test case is run ServiceTests::reset();