From 93ff01e1fcb1c582b223124d051dac18591201fa Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Tue, 12 Mar 2024 12:26:32 +0100 Subject: [PATCH] iox-#1755 Add tests for the platform logging --- .../include/iceoryx_platform/logging.hpp | 4 + iceoryx_platform/generic/source/logging.cpp | 11 +- .../moduletests/test_platform_logging.cpp | 155 ++++++++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 iceoryx_platform/test/moduletests/test_platform_logging.cpp diff --git a/iceoryx_platform/generic/include/iceoryx_platform/logging.hpp b/iceoryx_platform/generic/include/iceoryx_platform/logging.hpp index a4500654cc..be2d7b986a 100644 --- a/iceoryx_platform/generic/include/iceoryx_platform/logging.hpp +++ b/iceoryx_platform/generic/include/iceoryx_platform/logging.hpp @@ -46,6 +46,10 @@ void iox_platform_set_log_backend(IceoryxPlatformLogBackend log_backend); void iox_platform_detail_log( const char* file, int line, const char* function, IceoryxPlatformLogLevel log_level, const char* msg); +/// @note Implementation detail! Do not use directly +void iox_platform_detail_default_log_backend( + const char* file, int line, const char* function, IceoryxPlatformLogLevel log_level, const char* msg); + // NOLINTJUSTIFICATION The functionality of the macro (obtaining the source location) cannot be achieved with C++17 // NOLINTBEGIN(cppcoreguidelines-macro-usage) /// @brief Frontend for logging in the iceoryx platform diff --git a/iceoryx_platform/generic/source/logging.cpp b/iceoryx_platform/generic/source/logging.cpp index 95cc79b588..e85e5c94c2 100644 --- a/iceoryx_platform/generic/source/logging.cpp +++ b/iceoryx_platform/generic/source/logging.cpp @@ -22,11 +22,9 @@ #include #include -namespace -{ // NOLINTJUSTIFICATION only used in this file; should be fine // NOLINTNEXTLINE(readability-function-size) -void default_log_backend( +void iox_platform_detail_default_log_backend( const char* file, int line, const char* function, IceoryxPlatformLogLevel log_level, const char* msg) { if (log_level == IceoryxPlatformLogLevel::IOX_PLATFORM_LOG_LEVEL_OFF) @@ -35,7 +33,6 @@ void default_log_backend( } std::stringstream stream; - stream << file << ":" << line << " (" << function << ") "; switch (log_level) { @@ -62,13 +59,15 @@ void default_log_backend( break; } - stream << " " << msg; + stream << " " << file << ":" << line << " { " << function << " } " << msg; // NOLINTJUSTIFICATION We want to flush the line with each log output; if performance matters a custom log backend can be used, e.g. the logger in hoofs // NOLINTNEXTLINE(performance-avoid-endl) std::cout << stream.str() << std::endl; } +namespace +{ enum class LoggerExchangeState : uint8_t { DEFAULT, @@ -78,7 +77,7 @@ enum class LoggerExchangeState : uint8_t struct IceoryxPlatformLogger { - std::atomic log_backend{&default_log_backend}; + std::atomic log_backend{&iox_platform_detail_default_log_backend}; std::atomic logger_exchange_state{LoggerExchangeState::DEFAULT}; }; diff --git a/iceoryx_platform/test/moduletests/test_platform_logging.cpp b/iceoryx_platform/test/moduletests/test_platform_logging.cpp new file mode 100644 index 0000000000..db69a8410f --- /dev/null +++ b/iceoryx_platform/test/moduletests/test_platform_logging.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2024 by Mathias Kraus . 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 + +#include "iceoryx_platform/logging.hpp" + +#include "test.hpp" + +#include +#include +#include +#include +#include +#include + +namespace +{ +using namespace ::testing; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) for testing only +std::atomic has_custom_backend{false}; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) for testing only +std::atomic dummy_backend_output_counter{0}; + +class LogOutput +{ + public: + void set(IceoryxPlatformLogLevel log_level, const char* msg) noexcept + { + std::lock_guard lock(m_mtx); + + m_log_level = log_level; + m_log_msg = msg; + } + + void clear() noexcept + { + std::lock_guard lock(m_mtx); + + m_log_level = IceoryxPlatformLogLevel::IOX_PLATFORM_LOG_LEVEL_TRACE; + m_log_msg.clear(); + } + + std::pair get() const noexcept + { + std::lock_guard lock(m_mtx); + + return std::make_pair(m_log_level, m_log_msg); + } + + private: + mutable std::mutex m_mtx; + IceoryxPlatformLogLevel m_log_level{IceoryxPlatformLogLevel::IOX_PLATFORM_LOG_LEVEL_TRACE}; + std::string m_log_msg; +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) for testing only +LogOutput last_log_output; + +// NOLINTNEXTLINE(readability-function-size) not directly called; just for testing +void custom_log_backend( + const char* file, int line, const char* function, IceoryxPlatformLogLevel log_level, const char* msg) +{ + has_custom_backend = true; + last_log_output.set(log_level, msg); + iox_platform_detail_default_log_backend(file, line, function, log_level, msg); +} + +// NOLINTNEXTLINE(readability-function-size) not directly called; just for testing +void dummy_log_backend( + const char* file, int line, const char* function, IceoryxPlatformLogLevel log_level, const char* msg) +{ + dummy_backend_output_counter.fetch_add(1); + last_log_output.set(log_level, msg); + iox_platform_detail_default_log_backend(file, line, function, log_level, msg); +} + +TEST(Logging_test, DefaultBackendLogsToCout) +{ + ::testing::Test::RecordProperty("TEST_ID", "a1d5fbb5-38e3-4829-bf1a-4ea312115f12"); + + ASSERT_FALSE(has_custom_backend); + + std::stringstream buffer; + std::streambuf* original_cout_buffer = std::cout.rdbuf(buffer.rdbuf()); + + constexpr const char* MESSAGE{"Hypnotoad will rock you"}; + + IOX_PLATFORM_LOG(IOX_PLATFORM_LOG_LEVEL_INFO, MESSAGE); + + std::string log = buffer.str(); + EXPECT_THAT(log, HasSubstr(MESSAGE)); + + std::cout.rdbuf(original_cout_buffer); +} + +TEST(Logging_test, SettingBackendWithNullptrFails) +{ + ::testing::Test::RecordProperty("TEST_ID", "85029c9d-3be2-463c-838a-9fce01e10640"); + + ASSERT_FALSE(has_custom_backend); + + iox_platform_set_log_backend(nullptr); + + IOX_PLATFORM_LOG(IOX_PLATFORM_LOG_LEVEL_INFO, "Hypnotoad"); + ASSERT_FALSE(has_custom_backend); +} + +TEST(Logging_test, SettingCustomBackendWorks) +{ + ::testing::Test::RecordProperty("TEST_ID", "9348e6ef-30a4-4cd7-a3d3-264efe378fba"); + + ASSERT_FALSE(has_custom_backend); + + iox_platform_set_log_backend(&custom_log_backend); + + last_log_output.clear(); + + constexpr const char* MESSAGE{"Who will rock you?"}; + IOX_PLATFORM_LOG(IOX_PLATFORM_LOG_LEVEL_INFO, MESSAGE); + + auto [log_level, log_msg] = last_log_output.get(); + EXPECT_THAT(log_level, Eq(IOX_PLATFORM_LOG_LEVEL_INFO)); + EXPECT_THAT(log_msg, StrEq(MESSAGE)); +} + +TEST(Logging_test, SettingCustomBackendTwiceFails) +{ + ::testing::Test::RecordProperty("TEST_ID", "cbc3446e-5c02-4f46-933f-c91627f669d3"); + + ASSERT_TRUE(has_custom_backend); + + constexpr uint64_t DUMMY_MESSAGE_COUNT_AFTER_FAILED_SETUP{1}; + ASSERT_THAT(dummy_backend_output_counter, Eq(0)); + iox_platform_set_log_backend(&dummy_log_backend); + ASSERT_THAT(dummy_backend_output_counter, Eq(DUMMY_MESSAGE_COUNT_AFTER_FAILED_SETUP)); + + IOX_PLATFORM_LOG(IOX_PLATFORM_LOG_LEVEL_INFO, "Hypnotoad"); + + ASSERT_THAT(dummy_backend_output_counter, Eq(DUMMY_MESSAGE_COUNT_AFTER_FAILED_SETUP)); +} + +} // namespace