14#ifndef IOX2_BB_TESTING_TESTING_LOGGER_HPP
15#define IOX2_BB_TESTING_TESTING_LOGGER_HPP
21#include <gmock/gmock.h>
22#include <gtest/gtest.h>
40class LogPrinter :
public ::testing::EmptyTestEventListener {
42 auto OnTestStart(const ::testing::TestInfo& info) ->
void override;
43 auto OnTestPartResult(const ::testing::TestPartResult& result) ->
void override;
47 struct sigaction m_sigsegv_old_action = {};
48 struct sigaction m_sigfpe_old_action = {};
49 struct sigaction m_sigabrt_old_action = {};
91 static auto init() noexcept ->
void {
95 log::Logger::setActiveLogger(LOGGER);
96 log::Logger::init(log::logLevelFromEnvOr(log::LogLevel::Trace));
98 const std::lock_guard<std::mutex> lock(LOGGER.m_loggerDataLock);
103 if (
const auto* allow_log_string = std::getenv(
"IOX2_TESTING_ALLOW_LOG")) {
104 if (log::equalStrings(allow_log_string,
"on") || log::equalStrings(allow_log_string,
"ON")) {
105 LOGGER.m_loggerData.allowLog =
true;
107 LOGGER.m_loggerData.allowLog =
false;
108 std::cout <<
"" << std::endl;
109 std::cout <<
"Invalid value for 'IOX2_TESTING_ALLOW_LOG' environment variable!'" << std::endl;
110 std::cout <<
"Found: " << allow_log_string << std::endl;
111 std::cout <<
"Allowed is one of: on, ON" << std::endl;
114 LOGGER.m_loggerData.allowLog =
false;
117 auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
119 listeners.Append(
new (std::nothrow)
LogPrinter);
124 const std::lock_guard<std::mutex> lock(m_loggerDataLock);
125 m_loggerData.buffer.clear();
130 const std::lock_guard<std::mutex> lock(m_loggerDataLock);
131 if (m_loggerData.buffer.empty()) {
134 puts(
"#### Log start ####");
135 for (
const auto&
log : m_loggerData.buffer) {
138 puts(
"#### Log end ####");
139 m_loggerData.buffer.clear();
147 const std::lock_guard<std::mutex> lock(logger.m_loggerDataLock);
148 return logger.m_loggerData.buffer.size();
154 legacy::log::LogLevel log_level,
const std::function<
void(
const std::vector<std::string>&)>& check) ->
void {
156 check(get_log_messages());
170 const std::lock_guard<std::mutex> lock(m_loggerDataLock);
171 const auto log_buffer = Base::getLogBuffer();
172 m_loggerData.buffer.emplace_back(log_buffer.buffer, log_buffer.writeIndex);
174 if (m_loggerData.allowLog) {
178 Base::assumeFlushed();
184 static auto get_log_messages() noexcept -> std::vector<std::
string> {
186 const std::lock_guard<std::mutex> lock(logger.m_loggerDataLock);
187 return logger.m_loggerData.buffer;
191 std::vector<std::string> buffer;
192 bool allowLog {
true };
195 std::mutex m_loggerDataLock;
196 LoggerData m_loggerData;
207 constexpr const char* COLOR_RESET {
"\033[m" };
209 std::cout << log::logLevelDisplayColor(log::LogLevel::Warn)
210 <<
"Catched signal: " << log::logLevelDisplayColor(log::LogLevel::Fatal);
213 std::cout <<
"SIGSEGV" << std::flush;
216 std::cout <<
"SIGFPE" << std::flush;
219 std::cout <<
"SIGABRT" << std::flush;
226 std::cout << COLOR_RESET <<
"\n\n" << std::flush;
228 dynamic_cast<TestingLogger&
>(log::Logger::get()).print_log_buffer();
231 << log::logLevelDisplayColor(log::LogLevel::Warn)
232 <<
"Aborting execution by causing a SIGSEV with 'longjmp' to prevent triggering the signal handler again!"
233 << COLOR_RESET <<
"\n"
236 constexpr int JMP_VALUE { 1 };
247 std::set_terminate([]() ->
auto {
248 std::cout <<
"Terminate called\n" << std::flush;
254 struct sigaction action = {};
255 memset(&action, 0,
sizeof(
struct sigaction));
256 sigemptyset(&action.sa_mask);
258 action.sa_flags = SA_NODEFER;
261 sigaction(SIGSEGV, &action, &m_sigsegv_old_action);
262 sigaction(SIGFPE, &action, &m_sigfpe_old_action);
263 sigaction(SIGABRT, &action, &m_sigabrt_old_action);
268 if (result.failed()) {
273 sigaction(SIGSEGV, &m_sigsegv_old_action,
nullptr);
274 m_sigsegv_old_action = {};
275 sigaction(SIGFPE, &m_sigfpe_old_action,
nullptr);
276 m_sigfpe_old_action = {};
277 sigaction(SIGABRT, &m_sigabrt_old_action,
nullptr);
278 m_sigabrt_old_action = {};
#define IOX2_MAYBE_UNUSED
IOX2_MAYBE_UNUSED adds the [[gnu::unused]] attribute when it is available for the current compiler or...
This class hooks into gTest to automatically clear the log messages on the start of a test an print t...
auto OnTestStart(const ::testing::TestInfo &info) -> void override
auto OnTestPartResult(const ::testing::TestPartResult &result) -> void override
This logger is used for tests. It caches all the log messages and prints them to the console when a t...
static constexpr auto does_logger_support_log_level(const legacy::log::LogLevel log_level) noexcept -> bool
Checks if the the LogLevel is above the minimal supported LogLevel compiled into the binary.
~TestingLogger() override=default
static auto check_log_message_if_log_level_is_supported(legacy::log::LogLevel log_level, const std::function< void(const std::vector< std::string > &)> &check) -> void
Runs the provided checker function for the collected log messages.
auto print_log_buffer() noexcept -> void
Prints all log messages from the internal cache. This is automatically done at the end of a failed te...
static auto get_number_of_log_messages() noexcept -> uint64_t
Number of caches log messages.
void flush() noexcept override
TestingLogger(TestingLogger &&)=delete
auto operator=(TestingLogger &&) -> TestingLogger &=delete
auto operator=(const TestingLogger &) -> TestingLogger &=delete
static auto init() noexcept -> void
Initialized the logger. This should be called in the main function of the test binary.
auto clear_log_buffer() noexcept -> void
Removes all log messages from the internal cache. This is automatically done at the start of each tes...
TestingLogger(const TestingLogger &)=delete
This class acts as common interface for the Logger. It provides the common functionality and inherits...
static Logger & get() noexcept
Access to the logger singleton instance.
static jmp_buf exit_jmp_buffer
static void sig_handler_flush_logger(int sig, siginfo_t *info IOX2_MAYBE_UNUSED, void *ucontext IOX2_MAYBE_UNUSED)
LogLevel
This enum defines the log levels used for logging.
static constexpr LogLevel MINIMAL_LOG_LEVEL
The minimal log level which will be compiled into the application. All log levels below this will be ...
internal::Logger< ConsoleLogger > TestingLoggerBase
void log(LogLevel log_level, const char *origin, const char *message)
Adds a log message to the logger.