mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-12 10:23:50 +01:00
Mostly a refactor of tests (#513)
This commit is contained in:
parent
055f5e4d21
commit
626191a62d
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@ -12,10 +12,9 @@
|
|||||||
"name": "(gdb) Start",
|
"name": "(gdb) Start",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/build/g3log-FATAL-contract",
|
"program": "${workspaceFolder}/build/test_signal",
|
||||||
"MIMode": "gdb",
|
"MIMode": "gdb",
|
||||||
"cwd": "${workspaceFolder}/build"
|
"cwd": "${workspaceFolder}/build""setupCommands": [
|
||||||
"setupCommands": [
|
|
||||||
{
|
{
|
||||||
"description": "Enable pretty-printing for gdb",
|
"description": "Enable pretty-printing for gdb",
|
||||||
"text": "-enable-pretty-printing",
|
"text": "-enable-pretty-printing",
|
||||||
@ -28,6 +27,5 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cmake.configureOnOpen": false,
|
"cmake.configureOnOpen": false,
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true,
|
||||||
}
|
"files.associations": {
|
||||||
|
"ostream": "cpp"
|
||||||
|
}
|
||||||
|
}
|
@ -242,6 +242,30 @@ namespace g3 {
|
|||||||
exit(signal_number);
|
exit(signal_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is intended to be async-signal-safe. Using write and STDERR_FILENO should be
|
||||||
|
// safe in a signal handler. ref: http://pubs.opengroup.org/onlinepubs/009695399/functions/write.html
|
||||||
|
|
||||||
|
// This function is intended to be async-signal-safe.
|
||||||
|
// It writes an error message to stderr using the write system call,
|
||||||
|
// which is listed as async-signal-safe by POSIX.
|
||||||
|
size_t writeErrorMessage(const char* message) {
|
||||||
|
if (message == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the length of the message without using std library strlen or similar
|
||||||
|
// this is to ensure async-signal-safe by POSIX
|
||||||
|
size_t length = 0;
|
||||||
|
for (const char* p = message; *p != '\0'; ++p) {
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the message to STDERR_FILENO in a single call.
|
||||||
|
// This assumes that the message is not too large for a single write.
|
||||||
|
auto bytes_written = write(STDERR_FILENO, message, length);
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
// restores the signal handler back to default
|
// restores the signal handler back to default
|
||||||
void restoreFatalHandlingToDefault() {
|
void restoreFatalHandlingToDefault() {
|
||||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||||
@ -274,7 +298,8 @@ namespace g3 {
|
|||||||
|
|
||||||
if (sigaction(signal_number, &(old_action_it->second), nullptr) < 0) {
|
if (sigaction(signal_number, &(old_action_it->second), nullptr) < 0) {
|
||||||
auto signalname = std::string("sigaction - ") + signalToStr(signal_number);
|
auto signalname = std::string("sigaction - ") + signalToStr(signal_number);
|
||||||
perror(signalname.c_str());
|
// https://man7.org/linux/man-pages/man7/signal-safety.7.html (see signal-safety)
|
||||||
|
internal::writeErrorMessage(signalname.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
gSavedSigActions.erase(old_action_it);
|
gSavedSigActions.erase(old_action_it);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "g3log/crashhandler.hpp"
|
#include "g3log/crashhandler.hpp"
|
||||||
#include "g3log/g3log.hpp"
|
#include "g3log/g3log.hpp"
|
||||||
@ -171,6 +172,13 @@ namespace g3 {
|
|||||||
raise(signal_number);
|
raise(signal_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FYI: Concept of async-signal-safe operations does not exist on windows
|
||||||
|
// we stick to perror for lack of better alternatives.
|
||||||
|
size_t writeErrorMessage(const char* message) {
|
||||||
|
perror(message);
|
||||||
|
return std::strlen(message);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore back to default fatal event handling
|
// Restore back to default fatal event handling
|
||||||
void restoreFatalHandlingToDefault() {
|
void restoreFatalHandlingToDefault() {
|
||||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||||
@ -181,19 +189,19 @@ namespace g3 {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
|
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
|
||||||
perror("signal - SIGABRT");
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
|
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
|
||||||
perror("signal - SIGABRT");
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
|
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
|
||||||
perror("signal - SIGABRT");
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGILL, SIG_DFL))
|
if (SIG_ERR == signal(SIGILL, SIG_DFL))
|
||||||
perror("signal - SIGABRT");
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
|
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
|
||||||
perror("signal - SIGABRT");
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,15 +220,15 @@ namespace g3 {
|
|||||||
if (!g_installed_thread_signal_handler) {
|
if (!g_installed_thread_signal_handler) {
|
||||||
g_installed_thread_signal_handler = true;
|
g_installed_thread_signal_handler = true;
|
||||||
if (SIG_ERR == signal(SIGTERM, signalHandler))
|
if (SIG_ERR == signal(SIGTERM, signalHandler))
|
||||||
perror("signal - SIGTERM");
|
internal::writeErrorMessage("signal - SIGTERM");
|
||||||
if (SIG_ERR == signal(SIGABRT, signalHandler))
|
if (SIG_ERR == signal(SIGABRT, signalHandler))
|
||||||
perror("signal - SIGABRT");
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
if (SIG_ERR == signal(SIGFPE, signalHandler))
|
if (SIG_ERR == signal(SIGFPE, signalHandler))
|
||||||
perror("signal - SIGFPE");
|
internal::writeErrorMessage("signal - SIGFPE");
|
||||||
if (SIG_ERR == signal(SIGSEGV, signalHandler))
|
if (SIG_ERR == signal(SIGSEGV, signalHandler))
|
||||||
perror("signal - SIGSEGV");
|
internal::writeErrorMessage("signal - SIGSEGV");
|
||||||
if (SIG_ERR == signal(SIGILL, signalHandler))
|
if (SIG_ERR == signal(SIGILL, signalHandler))
|
||||||
perror("signal - SIGILL");
|
internal::writeErrorMessage("signal - SIGILL");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -148,13 +148,13 @@ namespace g3 {
|
|||||||
message.get()->setExpression(boolean_expression);
|
message.get()->setExpression(boolean_expression);
|
||||||
|
|
||||||
if (internal::wasFatal(level)) {
|
if (internal::wasFatal(level)) {
|
||||||
saveFatalMessage(level, stack_trace, message, fatal_signal);
|
saveFatalMessage(stack_trace, message, fatal_signal);
|
||||||
} else {
|
} else {
|
||||||
pushMessageToLogger(message);
|
pushMessageToLogger(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveFatalMessage(const LEVELS& level, const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal) {
|
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal) {
|
||||||
auto fatalhook = g_fatal_pre_logging_hook;
|
auto fatalhook = g_fatal_pre_logging_hook;
|
||||||
// In case the fatal_pre logging actually will cause a crash in its turn
|
// In case the fatal_pre logging actually will cause a crash in its turn
|
||||||
// let's not do recursive crashing!
|
// let's not do recursive crashing!
|
||||||
|
@ -77,5 +77,6 @@ namespace g3 {
|
|||||||
* This is an internal only function. Do not use it elsewhere. It is triggered
|
* This is an internal only function. Do not use it elsewhere. It is triggered
|
||||||
* from g3log, g3LogWorker after flushing messages to file */
|
* from g3log, g3LogWorker after flushing messages to file */
|
||||||
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
|
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
|
||||||
|
size_t writeErrorMessage(const char* message);
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace g3
|
} // namespace g3
|
||||||
|
@ -106,7 +106,7 @@ namespace g3 {
|
|||||||
void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level,
|
void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level,
|
||||||
const char* boolean_expression, int fatal_signal, const char* stack_trace);
|
const char* boolean_expression, int fatal_signal, const char* stack_trace);
|
||||||
|
|
||||||
void saveFatalMessage(const LEVELS& level, const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal);
|
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal);
|
||||||
|
|
||||||
// forwards the message to all sinks
|
// forwards the message to all sinks
|
||||||
void pushMessageToLogger(LogMessagePtr log_entry);
|
void pushMessageToLogger(LogMessagePtr log_entry);
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
SET(OS_SPECIFIC_TEST test_crashhandler_windows)
|
SET(OS_SPECIFIC_TEST test_crashhandler_windows)
|
||||||
ENDIF(MSVC OR MINGW)
|
ENDIF(MSVC OR MINGW)
|
||||||
|
|
||||||
SET(tests_to_run test_message test_filechange test_io test_cpp_future_concepts test_concept_sink test_sink ${OS_SPECIFIC_TEST})
|
SET(tests_to_run test_message test_filechange test_io test_fatal test_signal test_cpp_future_concepts test_concept_sink test_sink ${OS_SPECIFIC_TEST})
|
||||||
SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp)
|
SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp)
|
||||||
include_directories(${DIR_UNIT_TEST})
|
include_directories(${DIR_UNIT_TEST})
|
||||||
|
|
||||||
|
349
test_unit/test_fatal.cpp
Normal file
349
test_unit/test_fatal.cpp
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/** ==========================================================================re
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/generated_definitions.hpp"
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
#include "g3log/logworker.hpp"
|
||||||
|
#include "testing_helpers.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <exception>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const std::string log_directory = "./";
|
||||||
|
const std::string t_info = "test INFO ";
|
||||||
|
const std::string t_info2 = "test INFO 123";
|
||||||
|
const std::string t_debug = "test DEBUG ";
|
||||||
|
const std::string t_debug3 = "test DEBUG 1.123456";
|
||||||
|
const std::string t_warning = "test WARNING ";
|
||||||
|
const std::string t_warning3 = "test WARNING yello";
|
||||||
|
|
||||||
|
std::atomic<size_t> g_fatal_counter = {0};
|
||||||
|
void fatalCounter() {
|
||||||
|
++g_fatal_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
using namespace testing_helpers;
|
||||||
|
TEST(LogTest, LOGF__FATAL) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
ASSERT_FALSE(mockFatalWasCalled());
|
||||||
|
LOGF(FATAL, "This message should throw %d", 0);
|
||||||
|
EXPECT_TRUE(mockFatalWasCalled());
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw 0")) << "\n****" << mockFatalMessage();
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
||||||
|
|
||||||
|
auto file_content = logger.resetAndRetrieveContent();
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "This message should throw 0")) << "\n****" << file_content;
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_FATAL_SIGNALHANDLING
|
||||||
|
|
||||||
|
TEST(LogTest, FatalSIGTERM__UsingDefaultHandler) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
g_fatal_counter.store(0);
|
||||||
|
g3::setFatalPreLoggingHook(fatalCounter);
|
||||||
|
raise(SIGTERM);
|
||||||
|
logger.reset();
|
||||||
|
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
namespace {
|
||||||
|
std::atomic<size_t> customFatalCounter = {0};
|
||||||
|
std::atomic<int> lastEncounteredSignal = {0};
|
||||||
|
void customSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
|
||||||
|
lastEncounteredSignal.store(signal_number);
|
||||||
|
++customFatalCounter;
|
||||||
|
}
|
||||||
|
void installCustomSIGTERM() {
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
sigemptyset(&action.sa_mask);
|
||||||
|
action.sa_sigaction = &customSignalHandler;
|
||||||
|
action.sa_flags = SA_SIGINFO;
|
||||||
|
sigaction(SIGTERM, &action, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic<bool> oldSigTermCheck = {false};
|
||||||
|
void customOldSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
|
||||||
|
lastEncounteredSignal.store(signal_number);
|
||||||
|
oldSigTermCheck.store(true);
|
||||||
|
}
|
||||||
|
void installCustomOldSIGTERM() {
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
sigemptyset(&action.sa_mask);
|
||||||
|
action.sa_sigaction = &customOldSignalHandler;
|
||||||
|
action.sa_flags = SA_SIGINFO;
|
||||||
|
sigaction(SIGTERM, &action, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Override of signal handling and testing of it should be fairly easy to port to windows
|
||||||
|
// ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_windows.cpp
|
||||||
|
// what is missing is the override of signals and custom installation of signals
|
||||||
|
// ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_unix.cpp
|
||||||
|
// functions: void restoreFatalHandlingToDefault()
|
||||||
|
// void overrideSetupSignals(const std::map<int, std::string> overrideSignals)
|
||||||
|
// void restoreSignalHandler(int signal_number)
|
||||||
|
//
|
||||||
|
// It would require some adding of unit test (see the test below)
|
||||||
|
// and good Windows experience. Since I am not currently working much on the Windows
|
||||||
|
// side I am reaching out to the community for this one:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// For the test to work the following code should be added in this test
|
||||||
|
//void customSignalHandler(int signal_number) {
|
||||||
|
// lastEncounteredSignal.store(signal_number);
|
||||||
|
// ++customFatalCounter;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void installCustomSIGTERM() {
|
||||||
|
// ASSERT_TRUE(SIG_ERR != signal(SIGTERM, customSignalHandler));
|
||||||
|
//}
|
||||||
|
|
||||||
|
TEST(LogTest, FatalSIGTERM__UsingCustomHandler) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
g_fatal_counter.store(0);
|
||||||
|
g3::setFatalPreLoggingHook(fatalCounter);
|
||||||
|
installCustomSIGTERM();
|
||||||
|
g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}});
|
||||||
|
|
||||||
|
installCustomSIGTERM();
|
||||||
|
EXPECT_EQ(customFatalCounter.load(), size_t{0});
|
||||||
|
EXPECT_EQ(lastEncounteredSignal.load(), 0);
|
||||||
|
|
||||||
|
raise(SIGTERM);
|
||||||
|
logger.reset();
|
||||||
|
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
|
||||||
|
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
|
||||||
|
EXPECT_EQ(customFatalCounter.load(), size_t{1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LogTest, FatalSIGTERM__VerifyingOldCustomHandler) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
g_fatal_counter.store(0);
|
||||||
|
customFatalCounter.store(0);
|
||||||
|
lastEncounteredSignal.store(0);
|
||||||
|
|
||||||
|
g3::setFatalPreLoggingHook(fatalCounter);
|
||||||
|
installCustomOldSIGTERM();
|
||||||
|
g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGTERM, "SIGTERM"}});
|
||||||
|
g3::restoreSignalHandler(SIGTERM); // revert SIGTERM installation
|
||||||
|
|
||||||
|
EXPECT_EQ(customFatalCounter.load(), size_t{0});
|
||||||
|
EXPECT_EQ(lastEncounteredSignal.load(), 0);
|
||||||
|
EXPECT_FALSE(oldSigTermCheck.load());
|
||||||
|
raise(SIGTERM);
|
||||||
|
logger.reset();
|
||||||
|
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
|
||||||
|
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
|
||||||
|
EXPECT_TRUE(oldSigTermCheck.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISABLE_FATAL_SIGNALHANDLING
|
||||||
|
#endif // !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
|
||||||
|
TEST(LogTest, LOG_preFatalLogging_hook) {
|
||||||
|
{
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
ASSERT_FALSE(mockFatalWasCalled());
|
||||||
|
g_fatal_counter.store(0);
|
||||||
|
g3::setFatalPreLoggingHook(fatalCounter);
|
||||||
|
LOG(FATAL) << "This message is fatal";
|
||||||
|
logger.reset();
|
||||||
|
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Now with no fatal pre-logging-hook
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
ASSERT_FALSE(mockFatalWasCalled());
|
||||||
|
g_fatal_counter.store(0);
|
||||||
|
LOG(FATAL) << "This message is fatal";
|
||||||
|
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LogTest, LOG_FATAL) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
ASSERT_FALSE(mockFatalWasCalled());
|
||||||
|
|
||||||
|
LOG(FATAL) << "This message is fatal";
|
||||||
|
EXPECT_TRUE(mockFatalWasCalled());
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal"))
|
||||||
|
<< "\ncontent: [[" << mockFatalMessage() << "]]";
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
||||||
|
|
||||||
|
logger.reset();
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "This message is fatal"));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
|
||||||
|
}
|
||||||
|
TEST(LogTest, LOGF_IF__FATAL) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
EXPECT_FALSE(mockFatalWasCalled());
|
||||||
|
LOGF_IF(FATAL, (2 < 3), "This message %s be worse", "could");
|
||||||
|
EXPECT_TRUE(mockFatalWasCalled());
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")) << "\n"
|
||||||
|
<< mockFatalMessage();
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message could be worse"));
|
||||||
|
|
||||||
|
logger.reset();
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "This message could be worse"));
|
||||||
|
}
|
||||||
|
TEST(LogTest, LOG_IF__FATAL) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
|
||||||
|
EXPECT_FALSE(mockFatalWasCalled());
|
||||||
|
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw. xyz ";
|
||||||
|
EXPECT_TRUE(mockFatalWasCalled());
|
||||||
|
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw. xyz "));
|
||||||
|
|
||||||
|
logger.reset();
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "This message should throw. xyz "));
|
||||||
|
}
|
||||||
|
TEST(LogTest, LOG_IF__FATAL__NO_THROW) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
LOG_IF(FATAL, (2 > 3)) << "This message%sshould NOT throw";
|
||||||
|
ASSERT_FALSE(mockFatalWasCalled());
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK_F
|
||||||
|
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
EXPECT_TRUE(mockFatalMessage().empty());
|
||||||
|
EXPECT_FALSE(mockFatalWasCalled());
|
||||||
|
|
||||||
|
CHECK(1 == 2);
|
||||||
|
EXPECT_FALSE(mockFatalMessage().empty());
|
||||||
|
EXPECT_TRUE(mockFatalWasCalled());
|
||||||
|
|
||||||
|
logger.reset();
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "CONTRACT")) << "**** " << mockFatalMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
std::string msg = "This message is added to throw %s and %s";
|
||||||
|
std::string arg1 = "message";
|
||||||
|
std::string arg3 = "log";
|
||||||
|
|
||||||
|
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg3.c_str());
|
||||||
|
logger.reset();
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
std::string msg = "This message is added to throw message and log";
|
||||||
|
CHECK(1 >= 2) << msg;
|
||||||
|
|
||||||
|
logger.reset();
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, msg));
|
||||||
|
}
|
||||||
|
TEST(CHECK, CHECK_ThatWontThrow) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
std::string msg = "This %s should never appear in the %s";
|
||||||
|
std::string msg3 = "This message should never appear in the log";
|
||||||
|
CHECK(1 == 1);
|
||||||
|
CHECK_F(1 == 1, msg.c_str(), "message", "log");
|
||||||
|
logger.reset();
|
||||||
|
EXPECT_FALSE(mockFatalWasCalled());
|
||||||
|
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
EXPECT_FALSE(verifyContent(file_content, msg3));
|
||||||
|
EXPECT_FALSE(verifyContent(mockFatalMessage(), msg3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CHECK, CHECK_runtimeError) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
|
||||||
|
g3::setFatalExitHandler([](g3::FatalMessagePtr msg) {
|
||||||
|
throw std::runtime_error("fatal test handler");
|
||||||
|
});
|
||||||
|
|
||||||
|
class dynamic_int_array {
|
||||||
|
std::unique_ptr<int[]> data_;
|
||||||
|
const int size_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit dynamic_int_array(int size) :
|
||||||
|
data_{std::make_unique<int[]>(size)},
|
||||||
|
size_(size) {}
|
||||||
|
|
||||||
|
int& at(int i) {
|
||||||
|
CHECK(i < size_);
|
||||||
|
|
||||||
|
// unreachable if i >= size_
|
||||||
|
return data_[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic_int_array arr{3};
|
||||||
|
|
||||||
|
EXPECT_THROW(arr.at(3) = 1, std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// see also test_io -- AddNonFatal
|
||||||
|
TEST(CustomLogLevels, AddFatal) {
|
||||||
|
RestoreFileLogger logger(log_directory);
|
||||||
|
const LEVELS DEADLY{FATAL.value + 1, {"DEADLY"}};
|
||||||
|
EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
|
||||||
|
g_fatal_counter.store(0);
|
||||||
|
ASSERT_FALSE(mockFatalWasCalled());
|
||||||
|
g3::setFatalPreLoggingHook(fatalCounter);
|
||||||
|
#ifdef G3_DYNAMIC_LOGGING
|
||||||
|
g3::only_change_at_initialization::addLogLevel(DEADLY, true);
|
||||||
|
#endif
|
||||||
|
// clang-format off
|
||||||
|
LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__;
|
||||||
|
// clang-format on
|
||||||
|
logger.reset();
|
||||||
|
ASSERT_TRUE(mockFatalWasCalled());
|
||||||
|
EXPECT_EQ(size_t{1}, g_fatal_counter.load());
|
||||||
|
|
||||||
|
std::string file_content = readFileToText(logger.logFile());
|
||||||
|
std::string expected;
|
||||||
|
expected += "DEADLY [test_fatal.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
|
||||||
|
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
|
||||||
|
<< "\n\nExpected: \n"
|
||||||
|
<< expected;
|
||||||
|
g_fatal_counter.store(0); // restore
|
||||||
|
}
|
@ -327,288 +327,6 @@ TEST(LogTest, LOG_IF) {
|
|||||||
EXPECT_TRUE(verifyContent(file_content, t_info2));
|
EXPECT_TRUE(verifyContent(file_content, t_info2));
|
||||||
EXPECT_FALSE(verifyContent(file_content, t_debug3));
|
EXPECT_FALSE(verifyContent(file_content, t_debug3));
|
||||||
}
|
}
|
||||||
TEST(LogTest, LOGF__FATAL) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
ASSERT_FALSE(mockFatalWasCalled());
|
|
||||||
LOGF(FATAL, "This message should throw %d", 0);
|
|
||||||
EXPECT_TRUE(mockFatalWasCalled());
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw 0")) << "\n****" << mockFatalMessage();
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
|
||||||
|
|
||||||
auto file_content = logger.resetAndRetrieveContent();
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "This message should throw 0")) << "\n****" << file_content;
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef DISABLE_FATAL_SIGNALHANDLING
|
|
||||||
|
|
||||||
TEST(LogTest, FatalSIGTERM__UsingDefaultHandler) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
g_fatal_counter.store(0);
|
|
||||||
g3::setFatalPreLoggingHook(fatalCounter);
|
|
||||||
raise(SIGTERM);
|
|
||||||
logger.reset();
|
|
||||||
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
namespace {
|
|
||||||
std::atomic<size_t> customFatalCounter = {0};
|
|
||||||
std::atomic<int> lastEncounteredSignal = {0};
|
|
||||||
void customSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
|
|
||||||
lastEncounteredSignal.store(signal_number);
|
|
||||||
++customFatalCounter;
|
|
||||||
}
|
|
||||||
void installCustomSIGTERM() {
|
|
||||||
struct sigaction action;
|
|
||||||
memset(&action, 0, sizeof(action));
|
|
||||||
sigemptyset(&action.sa_mask);
|
|
||||||
action.sa_sigaction = &customSignalHandler;
|
|
||||||
action.sa_flags = SA_SIGINFO;
|
|
||||||
sigaction(SIGTERM, &action, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::atomic<bool> oldSigTermCheck = {false};
|
|
||||||
void customOldSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
|
|
||||||
lastEncounteredSignal.store(signal_number);
|
|
||||||
oldSigTermCheck.store(true);
|
|
||||||
}
|
|
||||||
void installCustomOldSIGTERM() {
|
|
||||||
struct sigaction action;
|
|
||||||
memset(&action, 0, sizeof(action));
|
|
||||||
sigemptyset(&action.sa_mask);
|
|
||||||
action.sa_sigaction = &customOldSignalHandler;
|
|
||||||
action.sa_flags = SA_SIGINFO;
|
|
||||||
sigaction(SIGTERM, &action, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// Override of signal handling and testing of it should be fairly easy to port to windows
|
|
||||||
// ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_windows.cpp
|
|
||||||
// what is missing is the override of signals and custom installation of signals
|
|
||||||
// ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_unix.cpp
|
|
||||||
// functions: void restoreFatalHandlingToDefault()
|
|
||||||
// void overrideSetupSignals(const std::map<int, std::string> overrideSignals)
|
|
||||||
// void restoreSignalHandler(int signal_number)
|
|
||||||
//
|
|
||||||
// It would require some adding of unit test (see the test below)
|
|
||||||
// and good Windows experience. Since I am not currently working much on the Windows
|
|
||||||
// side I am reaching out to the community for this one:
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// For the test to work the following code should be added in this test
|
|
||||||
//void customSignalHandler(int signal_number) {
|
|
||||||
// lastEncounteredSignal.store(signal_number);
|
|
||||||
// ++customFatalCounter;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//void installCustomSIGTERM() {
|
|
||||||
// ASSERT_TRUE(SIG_ERR != signal(SIGTERM, customSignalHandler));
|
|
||||||
//}
|
|
||||||
|
|
||||||
TEST(LogTest, FatalSIGTERM__UsingCustomHandler) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
g_fatal_counter.store(0);
|
|
||||||
g3::setFatalPreLoggingHook(fatalCounter);
|
|
||||||
installCustomSIGTERM();
|
|
||||||
g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}});
|
|
||||||
|
|
||||||
installCustomSIGTERM();
|
|
||||||
EXPECT_EQ(customFatalCounter.load(), size_t{0});
|
|
||||||
EXPECT_EQ(lastEncounteredSignal.load(), 0);
|
|
||||||
|
|
||||||
raise(SIGTERM);
|
|
||||||
logger.reset();
|
|
||||||
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
|
|
||||||
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
|
|
||||||
EXPECT_EQ(customFatalCounter.load(), size_t{1});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LogTest, FatalSIGTERM__VerifyingOldCustomHandler) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
g_fatal_counter.store(0);
|
|
||||||
customFatalCounter.store(0);
|
|
||||||
lastEncounteredSignal.store(0);
|
|
||||||
|
|
||||||
g3::setFatalPreLoggingHook(fatalCounter);
|
|
||||||
installCustomOldSIGTERM();
|
|
||||||
g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGTERM, "SIGTERM"}});
|
|
||||||
g3::restoreSignalHandler(SIGTERM); // revert SIGTERM installation
|
|
||||||
|
|
||||||
EXPECT_EQ(customFatalCounter.load(), size_t{0});
|
|
||||||
EXPECT_EQ(lastEncounteredSignal.load(), 0);
|
|
||||||
EXPECT_FALSE(oldSigTermCheck.load());
|
|
||||||
raise(SIGTERM);
|
|
||||||
logger.reset();
|
|
||||||
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
|
|
||||||
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
|
|
||||||
EXPECT_TRUE(oldSigTermCheck.load());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(LogTest, LOG_preFatalLogging_hook) {
|
|
||||||
{
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
ASSERT_FALSE(mockFatalWasCalled());
|
|
||||||
g_fatal_counter.store(0);
|
|
||||||
g3::setFatalPreLoggingHook(fatalCounter);
|
|
||||||
LOG(FATAL) << "This message is fatal";
|
|
||||||
logger.reset();
|
|
||||||
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Now with no fatal pre-logging-hook
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
ASSERT_FALSE(mockFatalWasCalled());
|
|
||||||
g_fatal_counter.store(0);
|
|
||||||
LOG(FATAL) << "This message is fatal";
|
|
||||||
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LogTest, LOG_FATAL) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
ASSERT_FALSE(mockFatalWasCalled());
|
|
||||||
|
|
||||||
LOG(FATAL) << "This message is fatal";
|
|
||||||
EXPECT_TRUE(mockFatalWasCalled());
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal"))
|
|
||||||
<< "\ncontent: [[" << mockFatalMessage() << "]]";
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
|
||||||
|
|
||||||
logger.reset();
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "This message is fatal"));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
|
|
||||||
}
|
|
||||||
TEST(LogTest, LOGF_IF__FATAL) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
EXPECT_FALSE(mockFatalWasCalled());
|
|
||||||
LOGF_IF(FATAL, (2 < 3), "This message %s be worse", "could");
|
|
||||||
EXPECT_TRUE(mockFatalWasCalled());
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")) << "\n"
|
|
||||||
<< mockFatalMessage();
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message could be worse"));
|
|
||||||
|
|
||||||
logger.reset();
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "This message could be worse"));
|
|
||||||
}
|
|
||||||
TEST(LogTest, LOG_IF__FATAL) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
|
|
||||||
EXPECT_FALSE(mockFatalWasCalled());
|
|
||||||
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw. xyz ";
|
|
||||||
EXPECT_TRUE(mockFatalWasCalled());
|
|
||||||
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw. xyz "));
|
|
||||||
|
|
||||||
logger.reset();
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "This message should throw. xyz "));
|
|
||||||
}
|
|
||||||
TEST(LogTest, LOG_IF__FATAL__NO_THROW) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
LOG_IF(FATAL, (2 > 3)) << "This message%sshould NOT throw";
|
|
||||||
ASSERT_FALSE(mockFatalWasCalled());
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK_F
|
|
||||||
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
EXPECT_TRUE(mockFatalMessage().empty());
|
|
||||||
EXPECT_FALSE(mockFatalWasCalled());
|
|
||||||
|
|
||||||
CHECK(1 == 2);
|
|
||||||
EXPECT_FALSE(mockFatalMessage().empty());
|
|
||||||
EXPECT_TRUE(mockFatalWasCalled());
|
|
||||||
|
|
||||||
logger.reset();
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "CONTRACT")) << "**** " << mockFatalMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
std::string msg = "This message is added to throw %s and %s";
|
|
||||||
std::string arg1 = "message";
|
|
||||||
std::string arg3 = "log";
|
|
||||||
|
|
||||||
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg3.c_str());
|
|
||||||
logger.reset();
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
std::string msg = "This message is added to throw message and log";
|
|
||||||
CHECK(1 >= 2) << msg;
|
|
||||||
|
|
||||||
logger.reset();
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, msg));
|
|
||||||
}
|
|
||||||
TEST(CHECK, CHECK_ThatWontThrow) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
std::string msg = "This %s should never appear in the %s";
|
|
||||||
std::string msg3 = "This message should never appear in the log";
|
|
||||||
CHECK(1 == 1);
|
|
||||||
CHECK_F(1 == 1, msg.c_str(), "message", "log");
|
|
||||||
logger.reset();
|
|
||||||
EXPECT_FALSE(mockFatalWasCalled());
|
|
||||||
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
EXPECT_FALSE(verifyContent(file_content, msg3));
|
|
||||||
EXPECT_FALSE(verifyContent(mockFatalMessage(), msg3));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CHECK, CHECK_runtimeError) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
|
|
||||||
g3::setFatalExitHandler([](g3::FatalMessagePtr msg) {
|
|
||||||
throw std::runtime_error("fatal test handler");
|
|
||||||
});
|
|
||||||
|
|
||||||
class dynamic_int_array {
|
|
||||||
std::unique_ptr<int[]> data_;
|
|
||||||
const int size_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit dynamic_int_array(int size) :
|
|
||||||
data_{std::make_unique<int[]>(size)},
|
|
||||||
size_(size) {}
|
|
||||||
|
|
||||||
int& at(int i) {
|
|
||||||
CHECK(i < size_);
|
|
||||||
|
|
||||||
// unreachable if i >= size_
|
|
||||||
return data_[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
dynamic_int_array arr{3};
|
|
||||||
|
|
||||||
EXPECT_THROW(arr.at(3) = 1, std::runtime_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CustomLogLevels, AddANonFatal) {
|
TEST(CustomLogLevels, AddANonFatal) {
|
||||||
RestoreFileLogger logger(log_directory);
|
RestoreFileLogger logger(log_directory);
|
||||||
@ -628,32 +346,6 @@ TEST(CustomLogLevels, AddANonFatal) {
|
|||||||
<< expected;
|
<< expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CustomLogLevels, AddFatal) {
|
|
||||||
RestoreFileLogger logger(log_directory);
|
|
||||||
const LEVELS DEADLY{FATAL.value + 1, {"DEADLY"}};
|
|
||||||
EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
|
|
||||||
g_fatal_counter.store(0);
|
|
||||||
ASSERT_FALSE(mockFatalWasCalled());
|
|
||||||
g3::setFatalPreLoggingHook(fatalCounter);
|
|
||||||
#ifdef G3_DYNAMIC_LOGGING
|
|
||||||
g3::only_change_at_initialization::addLogLevel(DEADLY, true);
|
|
||||||
#endif
|
|
||||||
// clang-format off
|
|
||||||
LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__;
|
|
||||||
// clang-format on
|
|
||||||
logger.reset();
|
|
||||||
ASSERT_TRUE(mockFatalWasCalled());
|
|
||||||
EXPECT_EQ(size_t{1}, g_fatal_counter.load());
|
|
||||||
|
|
||||||
std::string file_content = readFileToText(logger.logFile());
|
|
||||||
std::string expected;
|
|
||||||
expected += "DEADLY [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
|
|
||||||
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
|
|
||||||
<< "\n\nExpected: \n"
|
|
||||||
<< expected;
|
|
||||||
g_fatal_counter.store(0); // restore
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_LOGGING
|
#ifdef G3_DYNAMIC_LOGGING
|
||||||
namespace {
|
namespace {
|
||||||
// Restore dynamic levels if turned off
|
// Restore dynamic levels if turned off
|
||||||
|
62
test_unit/test_signal.cpp
Normal file
62
test_unit/test_signal.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
|
||||||
|
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
class SignalHandlingTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
int original_stderr;
|
||||||
|
int pipefd[2];
|
||||||
|
FILE* temp_stderr;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
// Redirect stderr to a pipe
|
||||||
|
ASSERT_EQ(pipe(pipefd), 0);
|
||||||
|
original_stderr = dup(STDERR_FILENO);
|
||||||
|
ASSERT_NE(original_stderr, -1);
|
||||||
|
ASSERT_NE(dup2(pipefd[1], STDERR_FILENO), -1);
|
||||||
|
temp_stderr = fdopen(pipefd[1], "w");
|
||||||
|
setvbuf(temp_stderr, NULL, _IONBF, 0); // Disable buffering
|
||||||
|
|
||||||
|
// Set the read end of the pipe to non-blocking mode
|
||||||
|
// so we can verify when buffer is empty
|
||||||
|
int flags = fcntl(pipefd[0], F_GETFL, 0);
|
||||||
|
fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
// Restore the original stderr
|
||||||
|
fclose(temp_stderr);
|
||||||
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
dup2(original_stderr, STDERR_FILENO);
|
||||||
|
close(original_stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadStderr() {
|
||||||
|
char buffer[1024];
|
||||||
|
ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1);
|
||||||
|
if (bytes_read >= 0) {
|
||||||
|
buffer[bytes_read] = '\0'; // Null-terminate the string
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(SignalHandlingTest, WriteErrorMessage_WritesToStderr) {
|
||||||
|
const char* test_message = "Test error message";
|
||||||
|
g3::internal::writeErrorMessage(test_message);
|
||||||
|
std::string output = ReadStderr();
|
||||||
|
ASSERT_EQ(output, test_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SignalHandlingTest, WriteErrorMessage_Nullptr_DoesNotWriteToStderr) {
|
||||||
|
g3::internal::writeErrorMessage(nullptr);
|
||||||
|
std::string output = ReadStderr();
|
||||||
|
ASSERT_TRUE(output.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
Loading…
Reference in New Issue
Block a user