Skip to content

Commit

Permalink
Merge pull request eclipse-iceoryx#1100 from ApexAI/iox-#1099-separat…
Browse files Browse the repository at this point in the history
…e-test-part-and-call-error-handler-in-hoofs-once

iox-eclipse-iceoryx#1099 Separate test part from `ErrorHandler` and call `errorHandler()` in `iceoryx_hoofs` only once
  • Loading branch information
mossmaurice authored Mar 16, 2022
2 parents be4e079 + 28d6a5d commit 86deed9
Show file tree
Hide file tree
Showing 50 changed files with 465 additions and 434 deletions.
2 changes: 2 additions & 0 deletions doc/website/release-notes/iceoryx-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

**Refactoring:**

- Separate test specific code from `ErrorHandler` and templatize `setTemporaryErrorHandler()` [\#1099](https://github.com/eclipse-iceoryx/iceoryx/issues/1099)

**New API features:**

**API Breaking Changes:**
Expand Down
40 changes: 20 additions & 20 deletions iceoryx_binding_c/test/moduletests/test_c2cpp_enum_translation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ TEST(c2cpp_enum_translation_test, SubscriberState)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::subscriberState(iox_test_binding_c::maxUnderlyingCEnumValue<iox_SubscriberState>()),
iox::popo::SubscriberState::HAS_DATA);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_SUBSCRIBER_STATE_VALUE));
Expand Down Expand Up @@ -88,8 +88,8 @@ TEST(c2cpp_enum_translation_test, SubscriberEvent)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::subscriberEvent(iox_test_binding_c::maxUnderlyingCEnumValue<iox_SubscriberEvent>()),
iox::popo::SubscriberEvent::DATA_RECEIVED);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_SUBSCRIBER_EVENT_VALUE));
Expand Down Expand Up @@ -125,8 +125,8 @@ TEST(c2cpp_enum_translation_test, ConsumerTooSlowPolicy)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::consumerTooSlowPolicy(iox_test_binding_c::maxUnderlyingCEnumValue<iox_ConsumerTooSlowPolicy>()),
iox::popo::ConsumerTooSlowPolicy::DISCARD_OLDEST_DATA);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__UNDEFINED_STATE_IN_IOX_CONSUMER_TOO_SLOW_POLICY));
Expand Down Expand Up @@ -162,8 +162,8 @@ TEST(c2cpp_enum_translation_test, QueueFullPolicy)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::queueFullPolicy(iox_test_binding_c::maxUnderlyingCEnumValue<iox_QueueFullPolicy>()),
iox::popo::QueueFullPolicy::DISCARD_OLDEST_DATA);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__UNDEFINED_STATE_IN_IOX_QUEUE_FULL_POLICY));
Expand Down Expand Up @@ -195,8 +195,8 @@ TEST(c2cpp_enum_translation_test, ClientState)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::clientState(iox_test_binding_c::maxUnderlyingCEnumValue<iox_ClientState>()),
iox::popo::ClientState::HAS_RESPONSE);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_CLIENT_STATE_VALUE));
Expand Down Expand Up @@ -228,8 +228,8 @@ TEST(c2cpp_enum_translation_test, ClientEvent)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::clientEvent(iox_test_binding_c::maxUnderlyingCEnumValue<iox_ClientEvent>()),
iox::popo::ClientEvent::RESPONSE_RECEIVED);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_CLIENT_EVENT_VALUE));
Expand Down Expand Up @@ -261,8 +261,8 @@ TEST(c2cpp_enum_translation_test, ServerState)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::serverState(iox_test_binding_c::maxUnderlyingCEnumValue<iox_ServerState>()),
iox::popo::ServerState::HAS_REQUEST);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_SERVER_STATE_VALUE));
Expand Down Expand Up @@ -294,8 +294,8 @@ TEST(c2cpp_enum_translation_test, ServerEvent)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::serverEvent(iox_test_binding_c::maxUnderlyingCEnumValue<iox_ServerEvent>()),
iox::popo::ServerEvent::REQUEST_RECEIVED);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_SERVER_EVENT_VALUE));
Expand Down Expand Up @@ -328,8 +328,8 @@ TEST(c2cpp_enum_translation_test, ServiceDiscoveryEvent)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::serviceDiscoveryEvent(iox_test_binding_c::maxUnderlyingCEnumValue<iox_ServiceDiscoveryEvent>()),
iox::runtime::ServiceDiscoveryEvent::SERVICE_REGISTRY_CHANGED);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_SERVICE_DISCOVERY_EVENT_VALUE));
Expand Down Expand Up @@ -366,8 +366,8 @@ TEST(c2cpp_enum_translation_test, MessagingPattern)
// the clang sanitizer detects this successfully and this leads to termination, and with this the test fails
#if !defined(__clang__)
iox::Error errorValue = iox::Error::kNO_ERROR;
auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler(
[&](const iox::Error e, const std::function<void()>, const iox::ErrorLevel) { errorValue = e; });
auto errorHandlerGuard = iox::ErrorHandlerMock::setTemporaryErrorHandler<iox::Error>(
[&](const iox::Error e, const iox::ErrorLevel) { errorValue = e; });
EXPECT_EQ(c2cpp::messagingPattern(iox_test_binding_c::maxUnderlyingCEnumValue<iox_MessagingPattern>()),
iox::popo::MessagingPattern::PUB_SUB);
EXPECT_THAT(errorValue, Eq(iox::Error::kBINDING_C__C2CPP_ENUM_TRANSLATION_INVALID_MESSAGING_PATTERN_VALUE));
Expand Down
2 changes: 2 additions & 0 deletions iceoryx_binding_c/test/test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef IOX_BINDING_C_TEST_HPP
#define IOX_BINDING_C_TEST_HPP

#include "iceoryx_hoofs/testing/mocks/error_handler_mock.hpp"

#include <gmock/gmock.h>
#include <gtest/gtest.h>

Expand Down
15 changes: 9 additions & 6 deletions iceoryx_hoofs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ if(GTest_FOUND) # only GTest_FOUND, just in case someone want's to use iceoryx_h
add_library(iceoryx_hoofs_testing
STATIC
testing/mocks/time_mock.cpp
testing/mocks/error_handler_mock.cpp
testing/timing_test.cpp
testing/compile_test.cpp
)
Expand Down Expand Up @@ -113,12 +114,14 @@ if(GTest_FOUND) # only GTest_FOUND, just in case someone want's to use iceoryx_h
$<INSTALL_INTERFACE:include/${PREFIX}>
)

target_link_libraries(iceoryx_hoofs_testing PRIVATE
${CODE_COVERAGE_LIBS}
iceoryx_hoofs
GTest::gtest
GTest::gmock
${CMAKE_DL_LIBS}
target_link_libraries(iceoryx_hoofs_testing
PUBLIC
GTest::gtest
GTest::gmock
PRIVATE
${CODE_COVERAGE_LIBS}
iceoryx_hoofs
${CMAKE_DL_LIBS}
)

if(LINUX)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,21 @@
#ifndef IOX_HOOFS_ERROR_HANDLING_ERROR_HANDLING_HPP
#define IOX_HOOFS_ERROR_HANDLING_ERROR_HANDLING_HPP

/// @todo #1099 rename this file to error_handler.hpp

#include "iceoryx_hoofs/cxx/generic_raii.hpp"
#include "iceoryx_hoofs/cxx/vector.hpp"
#include "iceoryx_hoofs/log/logger.hpp"
#include "iceoryx_hoofs/log/logging.hpp"
#include "iceoryx_hoofs/log/logmanager.hpp"

#include <cassert>
#include <functional>
#include <iostream>
#include <mutex>

namespace iox
{
// clang-format off
#define ICEORYX_ERRORS(error) \
error(NO_ERROR)\
error(FILEREADER__FAILED_TO_OPEN_FILE) \
error(POSH__ROUDI_PROCESS_SHUTDOWN_FAILED) \
error(POSH__ROUDI_PROCESS_SEND_VIA_IPC_CHANNEL_FAILED)\
error(POSH__RUNTIME_FACTORY_IS_NOT_SET) \
Expand Down Expand Up @@ -168,8 +169,6 @@ namespace iox
error(ICEORYX_ROUDI_MEMORY_MANAGER__ROUDI_STILL_RUNNING) \
error(ICEORYX_ROUDI_MEMORY_MANAGER__FAILED_TO_ADD_PORTPOOL_MEMORY_BLOCK) \
error(ICEORYX_ROUDI_MEMORY_MANAGER__FAILED_TO_ADD_MANAGEMENT_MEMORY_BLOCK) \
error(MQ_UNKNOWN_MSG) \
error(MQ_INVALID_MSG) \
error(IPC_INTERFACE__UNABLE_TO_CREATE_APPLICATION_CHANNEL) \
error(IPC_INTERFACE__REG_ROUDI_NOT_AVAILABLE) \
error(IPC_INTERFACE__REG_UNABLE_TO_WRITE_TO_ROUDI_CHANNEL) \
Expand All @@ -178,11 +177,6 @@ namespace iox
error(IPC_INTERFACE__CHECK_MQ_MAPS_TO_FILE) \
error(IPC_INTERFACE__APP_WITH_SAME_NAME_STILL_RUNNING) \
error(IPC_INTERFACE__COULD_NOT_ACQUIRE_FILE_LOCK) \
error(POSIX_WRAPPER__FAILED_TO_CREATE_SEMAPHORE) \
error(POSIX_TIMER__FIRED_TIMER_BUT_STATE_IS_INVALID) \
error(POSIX_TIMER__TIMERPOOL_OVERFLOW) \
error(POSIX_TIMER__INCONSISTENT_STATE) \
error(POSIX_TIMER__CALLBACK_RUNTIME_EXCEEDS_RETRIGGER_TIME) \
error(BINDING_C__UNDEFINED_STATE_IN_IOX_QUEUE_FULL_POLICY) \
error(BINDING_C__UNDEFINED_STATE_IN_IOX_CONSUMER_TOO_SLOW_POLICY) \
error(BINDING_C__PUBLISHER_OPTIONS_NOT_INITIALIZED) \
Expand Down Expand Up @@ -210,12 +204,6 @@ enum class Error : uint32_t
ICEORYX_ERRORS(CREATE_ICEORYX_ERROR_ENUM)
};

/// @brief Convenience stream operator to easily use the Error enum with std::ostream
/// @param[in] stream sink to write the message to
/// @param[in] value to convert to a string literal
/// @return the reference to `stream` which was provided as input parameter
std::ostream& operator<<(std::ostream& stream, Error value) noexcept;

/// @brief the available error levels
/// FATAL
/// - Log message with FATAL
Expand All @@ -242,45 +230,18 @@ enum class ErrorLevel : uint32_t
MODERATE
};

using HandlerFunction = std::function<void(const Error error, const std::function<void()>, const ErrorLevel)>;

/// @brief This handler is needed for unit testing, special debugging cases and
/// other corner cases where we'd like to explicitly suppress the
/// error handling.
class ErrorHandler
{
friend void
errorHandler(const Error error, const std::function<void()>& errorCallBack, const ErrorLevel level) noexcept;

public:
static cxx::GenericRAII setTemporaryErrorHandler(const HandlerFunction& newHandler) noexcept;

static const char* toString(const Error error) noexcept;

protected:
static void reactOnErrorLevel(const ErrorLevel level, const char* errorText) noexcept;

private:
static void defaultHandler(const Error error,
const std::function<void()>& errorCallBack,
const ErrorLevel level = ErrorLevel::FATAL) noexcept;

static const char* ERROR_NAMES[];
static iox::HandlerFunction handler;
/// Needed, if you want to exchange the handler. Remember the old one and call it if it is not your error. The error
/// mock needs to be the last one exchanging the handler in tests.
static std::mutex handler_mutex;
};

/// @brief Howto use the error handler correctly
/// 1.) If the error you would like to handle is not listed in ICEORYX_ERRORS(error)\...
/// macro just add them like:
/// 1.) Use the macro ICEORYX_ERRORS(error) to create the enum for your component and
/// add new errors like:
/// error(MODULE_NAME__MY_FUNKY_ERROR)
/// Attention: Create an error after the following convention:
/// MODULE_NAME__A_CLEAR_BUT_SHORT_ERROR_DESCRIPTION
/// And a long name is alright!
///
/// 2.) Call errorHandler(Error::kMODULE_NAME__MY_FUNKY_ERROR);
/// 2.) Specialize the following methods for your NewEnumErrorType:
/// - const char* toString(const NewEnumErrorType error)
///
/// 3.) Call errorHandler(Error::kMODULE_NAME__MY_FUNKY_ERROR);
/// Please pay attention to the "k" prefix
/// The defaults for errorCallback and ErrorLevel can also be overwritten:
/// errorHandler(
Expand Down Expand Up @@ -309,9 +270,36 @@ class ErrorHandler
/// errorHandler(Error::kTEST__ASSERT_CALLED);
/// ASSERT_TRUE(called);
/// @endcode
/// @tparam[in] Error type which is used to report the error (typically an enum)
template <typename Error>
void errorHandler(const Error error,
const std::function<void()>& errorCallBack = std::function<void()>(),
const ErrorLevel level = ErrorLevel::FATAL) noexcept;

using HandlerFunction = std::function<void(const uint32_t, const char*, const ErrorLevel)>;

/// @brief This handler is needed for unit testing, special debugging cases and
/// other corner cases where we'd like to explicitly suppress the
/// error handling.
class ErrorHandler
{
template <typename Error>
friend void
errorHandler(const Error error, const std::function<void()>& errorCallBack, const ErrorLevel level) noexcept;

protected:
static void reactOnErrorLevel(const ErrorLevel level, const char* errorText) noexcept;

static void
defaultHandler(const uint32_t error, const char* errorName, const ErrorLevel level = ErrorLevel::FATAL) noexcept;

static iox::HandlerFunction handler;
};

const char* toString(const Error error) noexcept;

} // namespace iox

#include "iceoryx_hoofs/internal/error_handling/error_handling.inl"

#endif // IOX_HOOFS_ERROR_HANDLING_ERROR_HANDLING_HPP
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2020 - 2021 by Apex.AI Inc. All rights reserved.
// Copyright (c) 2020 - 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,7 @@
#ifndef IOX_HOOFS_CXX_VARIANT_QUEUE_INL
#define IOX_HOOFS_CXX_VARIANT_QUEUE_INL

#include "iceoryx_hoofs/error_handling/error_handling.hpp"
#include "iceoryx_hoofs/cxx/variant_queue.hpp"

namespace iox
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_ERROR_HANDLING_ERROR_HANDLING_INL
#define IOX_HOOFS_ERROR_HANDLING_ERROR_HANDLING_INL

#include "iceoryx_hoofs/error_handling/error_handling.hpp"

namespace iox
{
template <typename Error>
inline void errorHandler(const Error error,
const std::function<void()>& errorCallBack IOX_MAYBE_UNUSED,
const ErrorLevel level) noexcept
{
ErrorHandler::handler(static_cast<typename std::underlying_type<Error>::type>(error), toString(error), level);
}

} // namespace iox

#endif // IOX_HOOFS_ERROR_HANDLING_ERROR_HANDLING_INL
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -17,8 +17,6 @@
#ifndef IOX_HOOFS_FILE_READER_FILE_READER_HPP
#define IOX_HOOFS_FILE_READER_FILE_READER_HPP

#include "iceoryx_hoofs/error_handling/error_handling.hpp"

#include <fstream>

namespace iox
Expand Down
Loading

0 comments on commit 86deed9

Please sign in to comment.