Windows compatibility fixes

This commit is contained in:
Sebastian Messmer 2018-07-08 19:34:08 -07:00
parent 312ac2ec31
commit 1dd88f0a67
14 changed files with 265 additions and 87 deletions

View File

@ -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");
}
}
}

View File

@ -39,7 +39,9 @@ Data CFB_Cipher<BlockCipher, KeySize>::encrypt(const CryptoPP::byte *plaintext,
auto encryption = typename CryptoPP::CFB_Mode<BlockCipher>::Encryption(static_cast<const CryptoPP::byte*>(encKey.data()), encKey.BINARY_LENGTH, iv.data());
Data ciphertext(ciphertextSize(plaintextSize));
iv.ToBinary(ciphertext.data());
encryption.ProcessData(static_cast<CryptoPP::byte*>(ciphertext.data()) + IV_SIZE, plaintext, plaintextSize);
if (plaintextSize > 0) {
encryption.ProcessData(static_cast<CryptoPP::byte*>(ciphertext.data()) + IV_SIZE, plaintext, plaintextSize);
}
return ciphertext;
}
@ -53,7 +55,10 @@ boost::optional<Data> CFB_Cipher<BlockCipher, KeySize>::decrypt(const CryptoPP::
const CryptoPP::byte *ciphertextData = ciphertext + IV_SIZE;
auto decryption = typename CryptoPP::CFB_Mode<BlockCipher>::Decryption(static_cast<const CryptoPP::byte*>(encKey.data()), encKey.BINARY_LENGTH, ciphertextIV);
Data plaintext(plaintextSize(ciphertextSize));
decryption.ProcessData(static_cast<CryptoPP::byte*>(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<CryptoPP::byte*>(plaintext.data()), ciphertextData, plaintext.size());
}
return std::move(plaintext);
}

View File

@ -140,7 +140,7 @@ private:
}
};
constexpr std::initializer_list<size_t> SIZES = {0, 1, 1024, 4096, 10*1024*1024};
constexpr std::array<size_t, 5> 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) { \

View File

@ -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})

View File

@ -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
);
}

View File

@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <regex>
//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)")));
}
}

View File

@ -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 <Windows.h>
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

View File

@ -63,7 +63,7 @@ public:
TYPED_TEST_CASE_P(CipherTest);
constexpr std::initializer_list<unsigned int> SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520};
constexpr std::array<unsigned int, 7> SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520};
TYPED_TEST_P(CipherTest, Size) {
for (auto size: SIZES) {

View File

@ -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

View File

@ -1,4 +1,5 @@
#include "testutils/LoggingTest.h"
#include <regex>
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() {

View File

@ -1,4 +1,5 @@
#include "testutils/LoggingTest.h"
#include <regex>
/*
* 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.*")));
}

View File

@ -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);
}

View File

@ -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 <iostream>
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;
}

View File

@ -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) {