diff --git a/src/cpp-utils/assert/backtrace_windows.cpp b/src/cpp-utils/assert/backtrace_windows.cpp index 77a4743b..84e7621f 100644 --- a/src/cpp-utils/assert/backtrace_windows.cpp +++ b/src/cpp-utils/assert/backtrace_windows.cpp @@ -13,15 +13,63 @@ using namespace cpputils::logging; namespace cpputils { namespace { + std::string exception_code_string(DWORD exception_code) { +#define HANDLE_CODE(code) case code: return #code; + switch (exception_code) { + // List of exception codes taken from https://docs.microsoft.com/en-us/windows/desktop/Debug/getexceptioncode + HANDLE_CODE(EXCEPTION_ACCESS_VIOLATION) + HANDLE_CODE(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) + HANDLE_CODE(EXCEPTION_BREAKPOINT) + HANDLE_CODE(EXCEPTION_DATATYPE_MISALIGNMENT) + HANDLE_CODE(EXCEPTION_FLT_DENORMAL_OPERAND) + HANDLE_CODE(EXCEPTION_FLT_DIVIDE_BY_ZERO) + HANDLE_CODE(EXCEPTION_FLT_INEXACT_RESULT) + HANDLE_CODE(EXCEPTION_FLT_INVALID_OPERATION) + HANDLE_CODE(EXCEPTION_FLT_OVERFLOW) + HANDLE_CODE(EXCEPTION_FLT_STACK_CHECK) + HANDLE_CODE(EXCEPTION_FLT_UNDERFLOW) + HANDLE_CODE(EXCEPTION_GUARD_PAGE) + HANDLE_CODE(EXCEPTION_ILLEGAL_INSTRUCTION) + HANDLE_CODE(EXCEPTION_IN_PAGE_ERROR) + HANDLE_CODE(EXCEPTION_INT_DIVIDE_BY_ZERO) + HANDLE_CODE(EXCEPTION_INT_OVERFLOW) + HANDLE_CODE(EXCEPTION_INVALID_DISPOSITION) + HANDLE_CODE(EXCEPTION_INVALID_HANDLE) + HANDLE_CODE(EXCEPTION_NONCONTINUABLE_EXCEPTION) + HANDLE_CODE(EXCEPTION_PRIV_INSTRUCTION) + HANDLE_CODE(EXCEPTION_SINGLE_STEP) + HANDLE_CODE(EXCEPTION_STACK_OVERFLOW) + HANDLE_CODE(STATUS_UNWIND_CONSOLIDATE) + default: + std::ostringstream str; + str << "UNKNOWN_CODE(0x" << std::hex << exception_code << ")"; + return str.str(); + } +#undef HANDLE_CODE + } + + struct SymInitializeRAII final { + const HANDLE process; + const bool success; + + SymInitializeRAII() + : process(GetCurrentProcess()) + , success(::SymInitialize(process, NULL, TRUE)) { + } + + ~SymInitializeRAII() { + ::SymCleanup(process); + } + }; + std::string backtrace_to_string(CONTEXT* context_record) { std::ostringstream backtrace; - HANDLE process = GetCurrentProcess(); - if (!::SymInitialize(process, NULL, TRUE)) { + SymInitializeRAII sym; + if (!sym.success) { DWORD error = GetLastError(); backtrace << "[Can't get backtrace. SymInitialize failed with error code " << std::dec << error << "]\n"; - } - else { + } else { // Initialize stack walking. STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); @@ -48,7 +96,7 @@ namespace cpputils { int i = 0; while (StackWalk64(machine_type, - GetCurrentProcess(), + sym.process, GetCurrentThread(), &stack_frame, context_record, @@ -61,13 +109,13 @@ namespace cpputils { DWORD64 displacement = 0; - if (SymFromAddr(process, (DWORD64)stack_frame.AddrPC.Offset, &displacement, symbol)) + if (SymFromAddr(sym.process, (DWORD64)stack_frame.AddrPC.Offset, &displacement, symbol)) { IMAGEHLP_MODULE64 moduleInfo; std::memset(&moduleInfo, 0, sizeof(IMAGEHLP_MODULE64)); moduleInfo.SizeOfStruct = sizeof(moduleInfo); - if (::SymGetModuleInfo64(process, symbol->ModBase, &moduleInfo)) { + if (::SymGetModuleInfo64(sym.process, symbol->ModBase, &moduleInfo)) { backtrace << moduleInfo.ModuleName << ":"; } backtrace << "0x" << std::hex << (DWORD64)stack_frame.AddrPC.Offset << ": "; @@ -83,7 +131,7 @@ namespace cpputils { IMAGEHLP_LINE64 line; SymSetOptions(SYMOPT_LOAD_LINES); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - if (::SymGetLineFromAddr64(process, (DWORD64)stack_frame.AddrPC.Offset, &dwDisplacement, &line)) { + if (::SymGetLineFromAddr64(sym.process, (DWORD64)stack_frame.AddrPC.Offset, &dwDisplacement, &line)) { backtrace << " at " << line.FileName << ":" << std::dec << line.LineNumber; } else { @@ -101,7 +149,7 @@ namespace cpputils { LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { std::string backtrace = backtrace_to_string(pExceptionInfo->ContextRecord); - LOG(ERR, "Top level exception. Backtrace:\n{}", backtrace); + LOG(ERR, "Top level exception. Code: {}. Backtrace:\n{}", exception_code_string(pExceptionInfo->ExceptionRecord->ExceptionCode), backtrace); return EXCEPTION_CONTINUE_SEARCH; } @@ -116,7 +164,10 @@ namespace cpputils { } void showBacktraceOnCrash() { - SetUnhandledExceptionFilter(TopLevelExceptionHandler); + PVOID result = AddVectoredExceptionHandler(1, TopLevelExceptionHandler); + if (result == nullptr) { + throw std::runtime_error("Error setting top level exception handler"); + } } } diff --git a/src/cpp-utils/crypto/symmetric/CFB_Cipher.h b/src/cpp-utils/crypto/symmetric/CFB_Cipher.h index 0554faa8..c75fe3e9 100644 --- a/src/cpp-utils/crypto/symmetric/CFB_Cipher.h +++ b/src/cpp-utils/crypto/symmetric/CFB_Cipher.h @@ -39,7 +39,9 @@ Data CFB_Cipher::encrypt(const CryptoPP::byte *plaintext, auto encryption = typename CryptoPP::CFB_Mode::Encryption(static_cast(encKey.data()), encKey.BINARY_LENGTH, iv.data()); Data ciphertext(ciphertextSize(plaintextSize)); iv.ToBinary(ciphertext.data()); - encryption.ProcessData(static_cast(ciphertext.data()) + IV_SIZE, plaintext, plaintextSize); + if (plaintextSize > 0) { + encryption.ProcessData(static_cast(ciphertext.data()) + IV_SIZE, plaintext, plaintextSize); + } return ciphertext; } @@ -53,7 +55,10 @@ boost::optional CFB_Cipher::decrypt(const CryptoPP:: const CryptoPP::byte *ciphertextData = ciphertext + IV_SIZE; auto decryption = typename CryptoPP::CFB_Mode::Decryption(static_cast(encKey.data()), encKey.BINARY_LENGTH, ciphertextIV); Data plaintext(plaintextSize(ciphertextSize)); - decryption.ProcessData(static_cast(plaintext.data()), ciphertextData, plaintext.size()); + if (plaintext.size() > 0) { + // TODO Shouldn't we pass in ciphertextSize instead of plaintext.size() here as last argument (and also in the if above)? + decryption.ProcessData(static_cast(plaintext.data()), ciphertextData, plaintext.size()); + } return std::move(plaintext); } diff --git a/test/blockstore/testutils/BlockStoreTest_Size.h b/test/blockstore/testutils/BlockStoreTest_Size.h index 4ddf3eb8..a7697815 100644 --- a/test/blockstore/testutils/BlockStoreTest_Size.h +++ b/test/blockstore/testutils/BlockStoreTest_Size.h @@ -140,7 +140,7 @@ private: } }; -constexpr std::initializer_list SIZES = {0, 1, 1024, 4096, 10*1024*1024}; +constexpr std::array SIZES = {0, 1, 1024, 4096, 10*1024*1024}; #define TYPED_TEST_P_FOR_ALL_SIZES(TestName) \ TYPED_TEST_P(BlockStoreTest, TestName) { \ for (auto size: SIZES) { \ diff --git a/test/cpp-utils/CMakeLists.txt b/test/cpp-utils/CMakeLists.txt index 1d87df95..050efb35 100644 --- a/test/cpp-utils/CMakeLists.txt +++ b/test/cpp-utils/CMakeLists.txt @@ -53,6 +53,8 @@ set(SOURCES value_type/ValueTypeTest.cpp ) +add_executable(${PROJECT_NAME}_exit_status process/exit_status.cpp) + add_executable(${PROJECT_NAME} ${SOURCES}) target_link_libraries(${PROJECT_NAME} googletest cpp-utils) add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/test/cpp-utils/assert/assert_debug_test.cpp b/test/cpp-utils/assert/assert_debug_test.cpp index cc5b3f5e..f366df1b 100644 --- a/test/cpp-utils/assert/assert_debug_test.cpp +++ b/test/cpp-utils/assert/assert_debug_test.cpp @@ -18,9 +18,14 @@ TEST(AssertTest_DebugBuild, DiesIfFalse) { } TEST(AssertTest_DebugBuild, AssertMessage) { +#if defined(_MSC_VER) +constexpr const char* EXPECTED = R"(Assertion \[2==5\] failed in .*assert_debug_test.cpp:\d+: my message)"; +#else +constexpr const char* EXPECTED = R"(Assertion \[2==5\] failed in .*assert_debug_test.cpp:[0-9]+: my message)"; +#endif EXPECT_DEATH( ASSERT(2==5, "my message"), - "Assertion \\[2==5\\] failed in .*/assert_debug_test.cpp:[0-9]+: my message" + EXPECTED ); } diff --git a/test/cpp-utils/assert/assert_release_test.cpp b/test/cpp-utils/assert/assert_release_test.cpp index 2043d946..4d094ede 100644 --- a/test/cpp-utils/assert/assert_release_test.cpp +++ b/test/cpp-utils/assert/assert_release_test.cpp @@ -1,5 +1,6 @@ #include #include +#include //Include the ASSERT macro for a release build #ifndef NDEBUG @@ -26,9 +27,12 @@ TEST(AssertTest_ReleaseBuild, AssertMessage) { ASSERT(2==5, "my message"); FAIL(); } catch (const cpputils::AssertFailed &e) { - EXPECT_THAT(e.what(), MatchesRegex( - "Assertion \\[2==5\\] failed in .*/assert_release_test.cpp:26: my message.*" - )); + std::string msg = e.what(); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + /*EXPECT_THAT(e.what(), MatchesRegex( + R"(Assertion \[2==5\] failed in .*assert_release_test.cpp:27: my message)" + ));*/ + EXPECT_TRUE(std::regex_search(e.what(), std::regex(R"(Assertion \[2==5\] failed in .*assert_release_test.cpp:27: my message)"))); } } diff --git a/test/cpp-utils/assert/backtrace_test.cpp b/test/cpp-utils/assert/backtrace_test.cpp index 17c4903a..0617c59f 100644 --- a/test/cpp-utils/assert/backtrace_test.cpp +++ b/test/cpp-utils/assert/backtrace_test.cpp @@ -17,62 +17,110 @@ TEST(BacktraceTest, ContainsTopLevelLine) { } namespace { -void nullptr_access() { - cpputils::showBacktraceOnCrash(); - int* ptr = nullptr; - *ptr = 5; + void nullptr_access() { + cpputils::showBacktraceOnCrash(); + int* ptr = nullptr; + *ptr = 5; + } } -void raise_signal(int signal) { - cpputils::showBacktraceOnCrash(); - ::raise(signal); + +#if defined(_MSC_VER) +#include +namespace { + void raise_sigsegv() { + cpputils::showBacktraceOnCrash(); + ::RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL); + } + void raise_sigill() { + cpputils::showBacktraceOnCrash(); + ::RaiseException(EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_NONCONTINUABLE, 0, NULL); + } + void raise_code(DWORD exception_code) { + cpputils::showBacktraceOnCrash(); + ::RaiseException(exception_code, EXCEPTION_NONCONTINUABLE, 0, NULL); + } } +#else +namespace { + void raise_sigsegv() { + cpputils::showBacktraceOnCrash(); + ::raise(SIGSEGV); + } + void raise_sigabrt() { + cpputils::showBacktraceOnCrash(); + ::raise(SIGABRT); + } + void raise_sigill() { + cpputils::showBacktraceOnCrash(); + ::raise(SIGILL); + } } +#endif TEST(BacktraceTest, ShowBacktraceOnNullptrAccess) { EXPECT_DEATH( - nullptr_access(), - "cpputils::backtrace" + nullptr_access(), + "ShowBacktraceOnNullptrAccess_Test::TestBody" ); } TEST(BacktraceTest, ShowBacktraceOnSigSegv) { EXPECT_DEATH( - raise_signal(SIGSEGV), - "cpputils::backtrace" - ); -} - -TEST(BacktraceTest, ShowBacktraceOnSigAbrt) { - EXPECT_DEATH( - raise_signal(SIGABRT), - "cpputils::backtrace" + raise_sigsegv(), + "ShowBacktraceOnSigSegv_Test::TestBody" ); } TEST(BacktraceTest, ShowBacktraceOnSigIll) { - EXPECT_DEATH( - raise_signal(SIGILL), - "cpputils::backtrace" - ); + EXPECT_DEATH( + raise_sigill(), + "ShowBacktraceOnSigIll_Test::TestBody" + ); } -TEST(BacktraceTest, ShowBacktraceOnSigSegv_ShowsCorrectSignalName) { +#if !defined(_MSC_VER) +TEST(BacktraceTest, ShowBacktraceOnSigAbrt) { EXPECT_DEATH( - raise_signal(SIGSEGV), - "SIGSEGV" + raise_sigabrt(), + "ShowBacktraceOnSigAbrt_Test::TestBody" ); } TEST(BacktraceTest, ShowBacktraceOnSigAbrt_ShowsCorrectSignalName) { - EXPECT_DEATH( - raise_signal(SIGABRT), - "SIGABRT" - ); + EXPECT_DEATH( + raise_sigabrt(), + "SIGABRT" + ); +} +#endif + +#if !defined(_MSC_VER) +constexpr const char* sigsegv_message = "SIGSEGV"; +constexpr const char* sigill_message = "SIGILL"; +#else +constexpr const char* sigsegv_message = "EXCEPTION_ACCESS_VIOLATION"; +constexpr const char* sigill_message = "EXCEPTION_ILLEGAL_INSTRUCTION"; +#endif + +TEST(BacktraceTest, ShowBacktraceOnSigSegv_ShowsCorrectSignalName) { + EXPECT_DEATH( + raise_sigsegv(), + sigsegv_message + ); } TEST(BacktraceTest, ShowBacktraceOnSigIll_ShowsCorrectSignalName) { EXPECT_DEATH( - raise_signal(SIGILL), - "SIGILL" + raise_sigill(), + sigill_message ); } + +#if defined(_MSC_VER) +TEST(BacktraceTest, UnknownCode_ShowsCorrectSignalName) { + EXPECT_DEATH( + raise_code(0x12345678), + "UNKNOWN_CODE\\(0x12345678\\)" + ); +} +#endif diff --git a/test/cpp-utils/crypto/symmetric/CipherTest.cpp b/test/cpp-utils/crypto/symmetric/CipherTest.cpp index 10c8bfbe..d4025645 100644 --- a/test/cpp-utils/crypto/symmetric/CipherTest.cpp +++ b/test/cpp-utils/crypto/symmetric/CipherTest.cpp @@ -63,7 +63,7 @@ public: TYPED_TEST_CASE_P(CipherTest); -constexpr std::initializer_list SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520}; +constexpr std::array SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520}; TYPED_TEST_P(CipherTest, Size) { for (auto size: SIZES) { diff --git a/test/cpp-utils/data/DataTest.cpp b/test/cpp-utils/data/DataTest.cpp index f4f44cfe..788ff492 100644 --- a/test/cpp-utils/data/DataTest.cpp +++ b/test/cpp-utils/data/DataTest.cpp @@ -200,8 +200,12 @@ TEST_F(DataTest, LargesizeSize) { EXPECT_EQ(size, data.size()); } #else +#if defined(_MSC_VER) +#pragma message This is not a 64bit architecture. Large size data tests are disabled. +#else #warning This is not a 64bit architecture. Large size data tests are disabled. #endif +#endif TEST_F(DataTest, LoadingNonexistingFile) { TempFile file(false); // Pass false to constructor, so the tempfile is not created diff --git a/test/cpp-utils/logging/LoggingLevelTest.cpp b/test/cpp-utils/logging/LoggingLevelTest.cpp index 513389f2..9dcccb27 100644 --- a/test/cpp-utils/logging/LoggingLevelTest.cpp +++ b/test/cpp-utils/logging/LoggingLevelTest.cpp @@ -1,4 +1,5 @@ #include "testutils/LoggingTest.h" +#include using namespace cpputils::logging; using std::string; @@ -8,7 +9,9 @@ class LoggingLevelTest: public LoggingTest { public: void EXPECT_DEBUG_LOG_ENABLED() { LOG(DEBUG, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*"))); } void EXPECT_DEBUG_LOG_DISABLED() { @@ -18,7 +21,9 @@ public: void EXPECT_INFO_LOG_ENABLED() { LOG(INFO, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"))); } void EXPECT_INFO_LOG_DISABLED() { @@ -28,7 +33,9 @@ public: void EXPECT_WARNING_LOG_ENABLED() { LOG(WARN, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*"))); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); } void EXPECT_WARNING_LOG_DISABLED() { @@ -38,7 +45,9 @@ public: void EXPECT_ERROR_LOG_ENABLED() { LOG(ERR, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[error\\].*My log message.*"))); } void EXPECT_ERROR_LOG_DISABLED() { diff --git a/test/cpp-utils/logging/LoggingTest.cpp b/test/cpp-utils/logging/LoggingTest.cpp index 0b63dd2c..e5b81696 100644 --- a/test/cpp-utils/logging/LoggingTest.cpp +++ b/test/cpp-utils/logging/LoggingTest.cpp @@ -1,4 +1,5 @@ #include "testutils/LoggingTest.h" +#include /* * Contains test cases for the following logging interface: @@ -13,7 +14,9 @@ TEST_F(LoggingTest, DefaultLoggerIsStderr) { string output = captureStderr([]{ LOG(INFO, "My log message"); }); - EXPECT_THAT(output, MatchesRegex(".*\\[Log\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(output, MatchesRegex(".*\\[Log\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(output, std::regex(".*\\[Log\\].*\\[info\\].*My log message.*"))); } TEST_F(LoggingTest, SetLogger_NewLoggerIsUsed) { @@ -21,13 +24,17 @@ TEST_F(LoggingTest, SetLogger_NewLoggerIsUsed) { string output = captureStderr([]{ LOG(INFO, "My log message"); }); - EXPECT_THAT(output, MatchesRegex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(output, MatchesRegex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(output, std::regex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*"))); } TEST_F(LoggingTest, SetNonStderrLogger_LogsToNewLogger) { setLogger(mockLogger.get()); logger()->info("My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(output, MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"))); } TEST_F(LoggingTest, SetNonStderrLogger_DoesNotLogToStderr) { @@ -41,26 +48,34 @@ TEST_F(LoggingTest, SetNonStderrLogger_DoesNotLogToStderr) { TEST_F(LoggingTest, InfoLog) { setLogger(mockLogger.get()); LOG(INFO, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"))); } TEST_F(LoggingTest, WarningLog) { setLogger(mockLogger.get()); LOG(WARN, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*"))); } TEST_F(LoggingTest, DebugLog) { setLevel(DEBUG); setLogger(mockLogger.get()); LOG(DEBUG, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*"))); } TEST_F(LoggingTest, ErrorLog) { setLogger(mockLogger.get()); LOG(ERR, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[error\\].*My log message.*"))); } void logAndExit(const string &message) { @@ -82,37 +97,49 @@ TEST_F(LoggingTest, LoggingAlsoWorksAfterFork) { TEST_F(LoggingTest, MessageIsConstChar) { setLogger(mockLogger.get()); LOG(INFO, "My log message"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"))); } TEST_F(LoggingTest, MessageIsString) { setLogger(mockLogger.get()); string msg = "My log message"; LOG(INFO, msg); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"))); } TEST_F(LoggingTest, FormatWithStringPlaceholder) { setLogger(mockLogger.get()); string str = "placeholder"; LOG(INFO, "My log message: {}", str); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*"))); } TEST_F(LoggingTest, FormatWithConstCharPlaceholder) { setLogger(mockLogger.get()); LOG(INFO, "My log message: {}", "placeholder"); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*"))); } TEST_F(LoggingTest, FormatWithIntPlaceholder) { setLogger(mockLogger.get()); LOG(INFO, "My log message: {}", 4); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4.*"))); } TEST_F(LoggingTest, FormatWithMultiplePlaceholders) { setLogger(mockLogger.get()); LOG(INFO, "My log message: {}, {}, {}", 4, "then", true); - EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4, then, true.*")); + // For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string? + //EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4, then, true.*")); + EXPECT_TRUE(std::regex_search(mockLogger.capturedLog(), std::regex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4, then, true.*"))); } diff --git a/test/cpp-utils/process/SubprocessTest.cpp b/test/cpp-utils/process/SubprocessTest.cpp index f6764f12..a5b0e93d 100644 --- a/test/cpp-utils/process/SubprocessTest.cpp +++ b/test/cpp-utils/process/SubprocessTest.cpp @@ -4,86 +4,97 @@ using cpputils::Subprocess; using cpputils::SubprocessError; +namespace { +std::string exit_with_message_and_status(const char* message, int status) { +#if defined(_MSC_VER) + constexpr const char* executable = "cpp-utils-test_exit_status.exe"; +#else + constexpr const char* executable = "./test/cpp-utils/cpp-utils-test_exit_status"; +#endif + return std::string(executable) + " \"" + message + "\" " + std::to_string(status); +} +} + TEST(SubprocessTest, CheckCall_success_output) { - EXPECT_EQ("hello\n", Subprocess::check_call("echo hello").output); + EXPECT_EQ("hello", Subprocess::check_call(exit_with_message_and_status("hello", 0)).output); } TEST(SubprocessTest, CheckCall_successwithemptyoutput_output) { - EXPECT_EQ("", Subprocess::check_call("exit 0").output); + EXPECT_EQ("", Subprocess::check_call(exit_with_message_and_status("", 0)).output); } TEST(SubprocessTest, CheckCall_success_exitcode) { - EXPECT_EQ(0, Subprocess::check_call("echo hello").exitcode); + EXPECT_EQ(0, Subprocess::check_call(exit_with_message_and_status("hello", 0)).exitcode); } TEST(SubprocessTest, CheckCall_successwithemptyoutput_exitcode) { - EXPECT_EQ(0, Subprocess::check_call("exit 0").exitcode); + EXPECT_EQ(0, Subprocess::check_call(exit_with_message_and_status("", 0)).exitcode); } TEST(SubprocessTest, CheckCall_error) { EXPECT_THROW( - Subprocess::check_call("exit 1"), + Subprocess::check_call(exit_with_message_and_status("", 1)), SubprocessError ); } TEST(SubprocessTest, CheckCall_error5) { EXPECT_THROW( - Subprocess::check_call("exit 5"), + Subprocess::check_call(exit_with_message_and_status("", 5)), SubprocessError ); } TEST(SubprocessTest, CheckCall_errorwithoutput) { EXPECT_THROW( - Subprocess::check_call("echo hello; exit 1"), + Subprocess::check_call(exit_with_message_and_status("hello", 1)), SubprocessError ); } TEST(SubprocessTest, CheckCall_error5withoutput) { EXPECT_THROW( - Subprocess::check_call("echo hello; exit 5"), + Subprocess::check_call(exit_with_message_and_status("hello", 5)), SubprocessError ); } TEST(SubprocessTest, Call_success_exitcode) { - EXPECT_EQ(0, Subprocess::call("echo hello").exitcode); + EXPECT_EQ(0, Subprocess::call(exit_with_message_and_status("hello", 0)).exitcode); } TEST(SubprocessTest, Call_success_output) { - EXPECT_EQ("hello\n", Subprocess::call("echo hello").output); + EXPECT_EQ("hello", Subprocess::call(exit_with_message_and_status("hello", 0)).output); } TEST(SubprocessTest, Call_error_exitcode) { - EXPECT_EQ(1, Subprocess::call("exit 1").exitcode); + EXPECT_EQ(1, Subprocess::call(exit_with_message_and_status("", 1)).exitcode); } TEST(SubprocessTest, Call_error_output) { - EXPECT_EQ("", Subprocess::call("exit 1").output); + EXPECT_EQ("", Subprocess::call(exit_with_message_and_status("", 1)).output); } TEST(SubprocessTest, Call_error5_exitcode) { - EXPECT_EQ(5, Subprocess::call("exit 5").exitcode); + EXPECT_EQ(5, Subprocess::call(exit_with_message_and_status("", 5)).exitcode); } TEST(SubprocessTest, Call_error5_output) { - EXPECT_EQ("", Subprocess::call("exit 1").output); + EXPECT_EQ("", Subprocess::call(exit_with_message_and_status("", 1)).output); } TEST(SubprocessTest, Call_errorwithoutput_output) { - EXPECT_EQ("hello\n", Subprocess::call("echo hello; exit 1").output); + EXPECT_EQ("hello", Subprocess::call(exit_with_message_and_status("hello", 1)).output); } TEST(SubprocessTest, Call_errorwithoutput_exitcode) { - EXPECT_EQ(1, Subprocess::call("echo hello; exit 1").exitcode); + EXPECT_EQ(1, Subprocess::call(exit_with_message_and_status("hello", 1)).exitcode); } TEST(SubprocessTest, Call_error5withoutput_output) { - EXPECT_EQ("hello\n", Subprocess::call("echo hello; exit 5").output); + EXPECT_EQ("hello", Subprocess::call(exit_with_message_and_status("hello", 5)).output); } TEST(SubprocessTest, Call_error5withoutput_exitcode) { - EXPECT_EQ(5, Subprocess::call("echo hello; exit 5").exitcode); + EXPECT_EQ(5, Subprocess::call(exit_with_message_and_status("hello", 5)).exitcode); } diff --git a/test/cpp-utils/process/exit_status.cpp b/test/cpp-utils/process/exit_status.cpp new file mode 100644 index 00000000..bd114187 --- /dev/null +++ b/test/cpp-utils/process/exit_status.cpp @@ -0,0 +1,15 @@ +// This is a small executable that prints its first argument and exits with the exit status in its second argument + +#include + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Wrong number of arguments" << std::endl; + abort(); + } + + std::cout << argv[1]; + + int exit_status = std::atoi(argv[2]); + return exit_status; +} diff --git a/test/cpp-utils/system/HomedirTest.cpp b/test/cpp-utils/system/HomedirTest.cpp index bad5e32f..a19ec867 100644 --- a/test/cpp-utils/system/HomedirTest.cpp +++ b/test/cpp-utils/system/HomedirTest.cpp @@ -17,9 +17,6 @@ TEST(HomedirTest, AppDataDirIsValid) { auto dir = HomeDirectory::getXDGDataDir(); EXPECT_FALSE(dir.empty()); EXPECT_GE(std::distance(dir.begin(), dir.end()), 2u); // has at least two components - for(const auto& component : dir) { - EXPECT_TRUE(component.string() == "/" || bf::native(component.string())); - } } TEST(HomedirTest, FakeHomeDirectorySetsHomedirCorrectly) {