diff --git a/.circleci/config.yml b/.circleci/config.yml index df33cec3..b9e70445 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -95,7 +95,7 @@ references: save_cache: key: v4_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }} paths: - - /tmp/boost_1_58_0 + - /tmp/boost_1_65_1 upgrade_boost: &upgrade_boost run: name: Upgrade Boost @@ -104,10 +104,10 @@ references: export NUMCORES=`nproc` echo Using $NUMCORES cores # Download and prepare boost (only if not already present from cache) - if [ ! -d "/tmp/boost_1_58_0" ]; then + if [ ! -d "/tmp/boost_1_65_1" ]; then echo "Didn't find boost in cache. Downloading and building." - wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.tar.bz2/download - if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "7480ec713b0aa13f0ec990603e87e3b5c8d53f4411329b10fae37fc963b90aad12dbd9290a33c3669ae801e9012a68683eadff057591e9ca2ebcd22b1a67b5d1" ]; then + wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.65.1/boost_1_65_1.tar.bz2/download + if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "a9e6866d3bb3e7c198f442ff09f5322f58064dca79bc420f2f0168eb63964226dfbc4f034a5a5e5958281fdf7518a1b057c894fbda0b61fced59c1661bf30f1a" ]; then echo Correct sha512sum else echo Wrong sha512sum @@ -117,14 +117,14 @@ references: echo Extracting... tar -xf /tmp/boost.tar.bz2 -C /tmp rm -rf boost.tar.bz2 - cd /tmp/boost_1_58_0 + cd /tmp/boost_1_65_1 ./bootstrap.sh --with-toolset=${BUILD_TOOLSET} --with-libraries=filesystem,thread,chrono,program_options cd .. else echo Found boost in cache. Use cache and build. fi # Compile and install boost (if cached, this should be fast) - cd /tmp/boost_1_58_0 + cd /tmp/boost_1_65_1 sudo ./b2 toolset=${BUILD_TOOLSET} link=static cxxflags=-fPIC -d0 -j$NUMCORES install build_pre: &build_pre restore_cache: @@ -419,18 +419,6 @@ jobs: GTEST_ARGS: "" CMAKE_FLAGS: "-DUSE_WERROR=on" RUN_TESTS: false - gcc_werror: - <<: *job_definition - environment: - CC: gcc-8 - CXX: g++-8 - BUILD_TOOLSET: gcc - APT_COMPILER_PACKAGE: "g++-8" - CXXFLAGS: "-Werror" - BUILD_TYPE: "Release" - GTEST_ARGS: "" - CMAKE_FLAGS: "" - RUN_TESTS: false no_compatibility: <<: *job_definition environment: @@ -479,7 +467,7 @@ jobs: OMP_NUM_THREADS: "1" CXXFLAGS: "-O2 -fsanitize=thread -fno-omit-frame-pointer" BUILD_TYPE: "Debug" - GTEST_ARGS: "--gtest_filter=-LoggingTest.LoggingAlsoWorksAfterFork:AssertTest_DebugBuild.*:SignalCatcherTest.*_thenDies:SignalHandlerTest.*_thenDies:SignalHandlerTest.givenMultipleSigIntHandlers_whenRaising_thenCatchesCorrectSignal:CliTest_Setup.*:CliTest_IntegrityCheck.*:*/CliTest_WrongEnvironment.*:CliTest_Unmount.*" + GTEST_ARGS: "--gtest_filter=-LoggingTest.LoggingAlsoWorksAfterFork:AssertTest_*:BacktraceTest.*:SignalCatcherTest.*_thenDies:SignalHandlerTest.*_thenDies:SignalHandlerTest.givenMultipleSigIntHandlers_whenRaising_thenCatchesCorrectSignal:CliTest_Setup.*:CliTest_IntegrityCheck.*:*/CliTest_WrongEnvironment.*:CliTest_Unmount.*" CMAKE_FLAGS: "" RUN_TESTS: true clang_tidy: diff --git a/CMakeLists.txt b/CMakeLists.txt index 45682b44..fdbff715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,8 @@ cmake_policy(SET CMP0054 NEW) project(cryfs) -include(cmake-utils/utils.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake-utils) +include(utils) require_gcc_version(5.0) require_clang_version(4.0) @@ -47,7 +48,7 @@ if(MSVC) add_definitions(/bigobj) endif() -add_subdirectory(vendor) +add_subdirectory(vendor EXCLUDE_FROM_ALL) add_subdirectory(src) add_subdirectory(doc) add_subdirectory(test) diff --git a/ChangeLog.txt b/ChangeLog.txt index 25eed6ba..9faa688a 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,12 +1,23 @@ -Version 0.10.1 (unreleased) +Version 0.10.2 (unreleased) +--------------- + + +Version 0.10.1 --------------- Fixed bugs: * If file system migration encounters files or folders with the wrong format in the base directory, it now just ignores them instead of crashing. * When trying to migrate a file system from CryFS 0.9.3 or older, show an error message suggesting to first open it with 0.9.10 because we can't load that anymore. * The '--unmount-idle' parameter works again +* Fix building with boost 1.67 + +Compatibility: +* Fixed some incompatibilities with systems using the musl libc +* Use boost::stacktrace instead of libbacktrace to build stack traces. This fixes a segfault issue with platforms using libexecinfo and is generally more portable. Other: * Updated to crypto++ 8.1 +* Updated to DokanY 1.2.1 +* Unit tests can now be run from any directory Version 0.10.0 diff --git a/README.md b/README.md index 555afcf7..b0f4a684 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Requirements - GCC version >= 5.0 or Clang >= 4.0 - CMake version >= 3.0 - libcurl4 (including development headers) - - Boost libraries version >= 1.58 (including development headers) + - Boost libraries version >= 1.65.1 (including development headers) - filesystem - system - chrono @@ -60,7 +60,7 @@ Requirements You can use the following commands to install these requirements # Ubuntu - $ sudo apt-get install git g++ cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libssl-dev libfuse-dev python + $ sudo apt install git g++ cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libssl-dev libfuse-dev python # Fedora sudo dnf install git gcc-c++ cmake make libcurl-devel boost-devel boost-static openssl-devel fuse-devel python @@ -96,7 +96,7 @@ Building on Windows (experimental) Build with Visual Studio 2017 and pass in the following flags to CMake: - -DDOKAN_PATH=[dokan library location, e.g. "C:\Program Files\Dokan\DokanLibrary-1.1.0"] + -DDOKAN_PATH=[dokan library location, e.g. "C:\Program Files\Dokan\DokanLibrary-1.2.1"] -DBOOST_ROOT=[path to root of boost installation] If you set these variables correctly in the `CMakeSettings.json` file, you should be able to open the cryfs source folder with Visual Studio 2017. diff --git a/appveyor.yml b/appveyor.yml index 9f2857ae..46ef3d44 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,18 +25,18 @@ init: - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\%VisualStudioVersion%\Community\VC\Auxiliary\Build\vcvars%arch%.bat" install: - - choco install -y dokany --version 1.1.0.2000 --installargs INSTALLDEVFILES=1 + - choco install -y dokany --version 1.2.1.2000 --installargs INSTALLDEVFILES=1 - cmake --version build_script: - cmd: mkdir build - cmd: cd build # note: The cmake+ninja workflow requires us to set build type in both cmake commands ('cmake' and 'cmake --build'), otherwise the cryfs.exe will depend on debug versions of the visual studio c++ runtime (i.e. msvcp140d.dll) - - cmd: cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_65_1" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.1.0" + - cmd: cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_67_0" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.2.1" - cmd: cmake --build . --config %CONFIGURATION% - cmd: .\test\gitversion\gitversion-test.exe # cpp-utils-test disables ThreadDebuggingTest_ThreadName.*_thenIsCorrect because the appveyor image is too old to support the API needed for that - - cmd: cd .\test\cpp-utils\ && .\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect && cd ..\.. + - cmd: .\test\cpp-utils\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect #- cmd: .\test\fspp\fspp-test.exe - cmd: .\test\parallelaccessstore\parallelaccessstore-test.exe - cmd: .\test\blockstore\blockstore-test.exe diff --git a/cmake-utils/FindLibunwind.cmake b/cmake-utils/FindLibunwind.cmake new file mode 100644 index 00000000..a41f7dd6 --- /dev/null +++ b/cmake-utils/FindLibunwind.cmake @@ -0,0 +1,44 @@ +# Taken from https://github.com/monero-project/monero/blob/31bdf7bd113c2576fe579ef3a25a2d8fef419ffc/cmake/FindLibunwind.cmake +# modifications: +# - remove linkage against gcc_eh because it was causing segfaults in various of our unit tests + +# - Try to find libunwind +# Once done this will define +# +# LIBUNWIND_FOUND - system has libunwind +# LIBUNWIND_INCLUDE_DIR - the libunwind include directory +# LIBUNWIND_LIBRARIES - Link these to use libunwind +# LIBUNWIND_DEFINITIONS - Compiler switches required for using libunwind + +# Copyright (c) 2006, Alexander Dymo, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +find_path(LIBUNWIND_INCLUDE_DIR libunwind.h + /usr/include + /usr/local/include + ) + +find_library(LIBUNWIND_LIBRARIES NAMES unwind ) +if(NOT LIBUNWIND_LIBRARIES STREQUAL "LIBUNWIND_LIBRARIES-NOTFOUND") + if (CMAKE_COMPILER_IS_GNUCC) + set(LIBUNWIND_LIBRARIES "${LIBUNWIND_LIBRARIES}") + endif() +endif() + +# some versions of libunwind need liblzma, and we don't use pkg-config +# so we just look whether liblzma is installed, and add it if it is. +# It might not be actually needed, but doesn't hurt if it is not. +# We don't need any headers, just the lib, as it's privately needed. +message(STATUS "looking for liblzma") +find_library(LIBLZMA_LIBRARIES lzma ) +if(NOT LIBLZMA_LIBRARIES STREQUAL "LIBLZMA_LIBRARIES-NOTFOUND") + message(STATUS "liblzma found") + set(LIBUNWIND_LIBRARIES "${LIBUNWIND_LIBRARIES};${LIBLZMA_LIBRARIES}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libunwind "Could not find libunwind" LIBUNWIND_INCLUDE_DIR LIBUNWIND_LIBRARIES) +# show the LIBUNWIND_INCLUDE_DIR and LIBUNWIND_LIBRARIES variables only in the advanced view +mark_as_advanced(LIBUNWIND_INCLUDE_DIR LIBUNWIND_LIBRARIES ) \ No newline at end of file diff --git a/cmake-utils/utils.cmake b/cmake-utils/utils.cmake index 6648871c..da4dff8c 100644 --- a/cmake-utils/utils.cmake +++ b/cmake-utils/utils.cmake @@ -108,7 +108,7 @@ endfunction(target_enable_style_warnings) function(target_add_boost TARGET) # Load boost libraries if(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS) - # Many supported systems don't have boost >= 1.58. Better link it statically. + # Many supported systems don't have boost >= 1.65.1. Better link it statically. message(STATUS "Boost will be statically linked") set(Boost_USE_STATIC_LIBS ON) else(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS) @@ -116,7 +116,7 @@ function(target_add_boost TARGET) set(Boost_USE_STATIC_LIBS OFF) endif(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS) set(BOOST_THREAD_VERSION 4) - find_package(Boost 1.58.0 + find_package(Boost 1.65.1 REQUIRED COMPONENTS ${ARGN}) target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) diff --git a/src/cpp-utils/CMakeLists.txt b/src/cpp-utils/CMakeLists.txt index 2ecd690c..f66f99f8 100644 --- a/src/cpp-utils/CMakeLists.txt +++ b/src/cpp-utils/CMakeLists.txt @@ -61,13 +61,19 @@ set(SOURCES add_library(${PROJECT_NAME} STATIC ${SOURCES}) - -if(NOT MSVC) - find_package(Backtrace REQUIRED) - target_include_directories(${PROJECT_NAME} PUBLIC ${Backtrace_INCLUDE_DIRS}) - target_link_libraries(${PROJECT_NAME} PUBLIC ${Backtrace_LIBRARIES}) +if(MSVC) + target_link_libraries(${PROJECT_NAME} PUBLIC DbgHelp) +elseif (APPLE) + target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED) else() - target_link_libraries(${PROJECT_NAME} PUBLIC DbgHelp) + find_program(ADDR2LINE addr2line) + if ("${ADDR2LINE}" STREQUAL "ADDR2LINE-NOTFOUND") + message(WARNING "addr2line not found. Backtraces will be reduced.") + else() + message(STATUS "addr2line found. Using it for backtraces.") + target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_STACKTRACE_USE_ADDR2LINE) + target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_STACKTRACE_ADDR2LINE_LOCATION=${ADDR2LINE}) + endif() endif() if (NOT MSVC) @@ -85,6 +91,6 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS}) target_link_libraries(${PROJECT_NAME} PUBLIC spdlog cryptopp) -target_add_boost(${PROJECT_NAME} filesystem system thread) +target_add_boost(${PROJECT_NAME} filesystem system thread chrono) target_enable_style_warnings(${PROJECT_NAME}) target_activate_cpp14(${PROJECT_NAME}) diff --git a/src/cpp-utils/assert/backtrace_nonwindows.cpp b/src/cpp-utils/assert/backtrace_nonwindows.cpp index c495d0b6..8fbcac60 100644 --- a/src/cpp-utils/assert/backtrace_nonwindows.cpp +++ b/src/cpp-utils/assert/backtrace_nonwindows.cpp @@ -1,19 +1,12 @@ #if !defined(_MSC_VER) -#include "backtrace.h" -#include #include -#include -#include -#include -#include #include -#include -#include + #include "../logging/logging.h" #include -// TODO Add file and line number on non-windows +#include using std::string; using std::ostringstream; @@ -21,91 +14,36 @@ using namespace cpputils::logging; namespace cpputils { -namespace { - std::string demangle(const string &mangledName) { - string result; - int status = -10; - char *demangledName = nullptr; - try { - demangledName = abi::__cxa_demangle(mangledName.c_str(), NULL, NULL, &status); - if (status == 0) { - result = demangledName; - } else { - result = "[demangling error " + std::to_string(status) + "]" + mangledName; - } - free(demangledName); - return result; - } catch (...) { - free(demangledName); - throw; - } - } - - void pretty_print(std::ostream& str, const void *addr) { - Dl_info info; - if (0 == dladdr(addr, &info)) { - str << "[failed parsing line]"; - } else { - if (nullptr == info.dli_fname) { - str << "[no dli_fname]"; - } else { - str << info.dli_fname; - } - str << ":" << std::hex << info.dli_fbase << " "; - if (nullptr == info.dli_sname) { - str << "[no symbol name]"; - } else if (info.dli_sname[0] == '_') { - // is a mangled name - str << demangle(info.dli_sname); - } else { - // is not a mangled name - str << info.dli_sname; - } - str << " : " << std::hex << info.dli_saddr; - } - } - - string backtrace_to_string(void *array[], size_t size) { - ostringstream result; - for (size_t i = 0; i < size; ++i) { - result << "#" << std::dec << i << " "; - pretty_print(result, array[i]); - result << "\n"; - } - return result.str(); - } +string backtrace() { + std::ostringstream str; + str << boost::stacktrace::stacktrace(); + return str.str(); } - string backtrace() { - constexpr unsigned int MAX_SIZE = 100; - void *array[MAX_SIZE]; - size_t size = ::backtrace(array, MAX_SIZE); - return backtrace_to_string(array, size); - } - namespace { - void sigsegv_handler(int) { - LOG(ERR, "SIGSEGV\n{}", backtrace()); - exit(1); - } - void sigill_handler(int) { - LOG(ERR, "SIGILL\n{}", backtrace()); - exit(1); - } - void sigabrt_handler(int) { - LOG(ERR, "SIGABRT\n{}", backtrace()); - exit(1); - } +void sigsegv_handler(int) { + LOG(ERR, "SIGSEGV\n{}", backtrace()); + exit(1); +} +void sigill_handler(int) { + LOG(ERR, "SIGILL\n{}", backtrace()); + exit(1); +} +void sigabrt_handler(int) { + LOG(ERR, "SIGABRT\n{}", backtrace()); + exit(1); +} } - void showBacktraceOnCrash() { - // the signal handler RAII objects will be initialized on first call (which will register the signal handler) - // and destroyed on program exit (which will unregister the signal handler) +void showBacktraceOnCrash() { + // the signal handler RAII objects will be initialized on first call (which will register the signal handler) + // and destroyed on program exit (which will unregister the signal handler) + + static SignalHandlerRAII<&sigsegv_handler> segv(SIGSEGV); + static SignalHandlerRAII<&sigabrt_handler> abrt(SIGABRT); + static SignalHandlerRAII<&sigill_handler> ill(SIGILL); +} - static SignalHandlerRAII<&sigsegv_handler> segv(SIGSEGV); - static SignalHandlerRAII<&sigabrt_handler> abrt(SIGABRT); - static SignalHandlerRAII<&sigill_handler> ill(SIGILL); - } } #endif diff --git a/src/cpp-utils/process/SignalCatcher.cpp b/src/cpp-utils/process/SignalCatcher.cpp index c3fccb30..ea718a5d 100644 --- a/src/cpp-utils/process/SignalCatcher.cpp +++ b/src/cpp-utils/process/SignalCatcher.cpp @@ -88,7 +88,8 @@ public: , _registerer(signal, this) , _handler(signal) { // note: the order of the members ensures that: - // - when registering the signal handler fails, the registerer will be destroyed, unregistering the signal_occurred_flag, + // - when registering the signal handler, the SignalCatcher impl already has a valid _signal_occurred_flag set. + // - when registering the signal handler fails, the _registerer will be destroyed again, unregistering this SignalCatcherImpl, // i.e. there is no leak. // Allow only the set of signals that is supported on all platforms, see for Windows: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal?view=vs-2017 diff --git a/src/cpp-utils/thread/debugging.h b/src/cpp-utils/thread/debugging.h index 9965649c..d70e5f2f 100644 --- a/src/cpp-utils/thread/debugging.h +++ b/src/cpp-utils/thread/debugging.h @@ -9,7 +9,11 @@ namespace cpputils { void set_thread_name(const char* name); std::string get_thread_name(); + +#if defined(__GLIBC__) || defined(__APPLE__) || defined(_MSC_VER) +// this is not supported on musl systems, that's why we ifdef it for glibc only. std::string get_thread_name(std::thread* thread); +#endif } diff --git a/src/cpp-utils/thread/debugging_nonwindows.cpp b/src/cpp-utils/thread/debugging_nonwindows.cpp index 3ed543ff..4afb4045 100644 --- a/src/cpp-utils/thread/debugging_nonwindows.cpp +++ b/src/cpp-utils/thread/debugging_nonwindows.cpp @@ -5,6 +5,14 @@ #include #include #include +#if !(defined(__GLIBC__) || defined(__APPLE__)) +// for pthread_getname_np_gcompat +#include // errno +#include // O_CLOEXEC, O_RDONLY +#include // open, read +#include +#include +#endif namespace cpputils { @@ -28,9 +36,47 @@ void set_thread_name(const char* name) { } namespace { +#if !(defined(__GLIBC__) || defined(__APPLE__)) + +struct OpenFileRAII final { + explicit OpenFileRAII(const char* filename) : fd(::open(filename, O_RDONLY | O_CLOEXEC)) {} + ~OpenFileRAII() { + int result = close(fd); + if (result != 0) { + throw std::runtime_error("Error closing file. Errno: " + std::to_string(errno)); + } + } + + int fd; +}; + +// get the name of a thread +int pthread_getname_np_gcompat(pthread_t thread, char *name, size_t len) { + ASSERT(thread == pthread_self(), "On musl systems, it's only supported to get the name of the current thread."); + + auto file = OpenFileRAII("/proc/thread-self/comm"); + ssize_t n; + if (file.fd < 0) + return errno; + n = read(file.fd, name, len); + if (n < 0) + return errno; + // if the trailing newline was not read, the buffer was too small + if (n == 0 || name[n - 1] != '\n') + return ERANGE; + // null-terminate string + name[n - 1] = '\0'; + return 0; +} +#endif + std::string get_thread_name(pthread_t thread) { char name[MAX_NAME_LEN]; +#if defined(__GLIBC__) || defined(__APPLE__) int result = pthread_getname_np(thread, name, MAX_NAME_LEN); +#else + int result = pthread_getname_np_gcompat(thread, name, MAX_NAME_LEN); +#endif if (0 != result) { throw std::runtime_error("Error getting thread name with pthread_getname_np. Code: " + std::to_string(result)); } @@ -39,16 +85,19 @@ std::string get_thread_name(pthread_t thread) { name[MAX_NAME_LEN - 1] = '\0'; return name; } + } std::string get_thread_name() { return get_thread_name(pthread_self()); } +#if defined(__GLIBC__) || defined(__APPLE__) std::string get_thread_name(std::thread* thread) { ASSERT(thread->joinable(), "Thread not running"); return get_thread_name(thread->native_handle()); } +#endif } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d8d77fa8..4879389e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,7 @@ if (BUILD_TESTING) include_directories(../src) + add_subdirectory(my-gtest-main) add_subdirectory(gitversion) add_subdirectory(cpp-utils) if (NOT MSVC) diff --git a/test/blobstore/CMakeLists.txt b/test/blobstore/CMakeLists.txt index f9eeb8d7..05e98b8d 100644 --- a/test/blobstore/CMakeLists.txt +++ b/test/blobstore/CMakeLists.txt @@ -27,7 +27,7 @@ set(SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest blobstore) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest blobstore) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/test/blockstore/CMakeLists.txt b/test/blockstore/CMakeLists.txt index 54167aef..ca63acce 100644 --- a/test/blockstore/CMakeLists.txt +++ b/test/blockstore/CMakeLists.txt @@ -42,7 +42,7 @@ set(SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest blockstore) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest blockstore) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/test/cpp-utils/CMakeLists.txt b/test/cpp-utils/CMakeLists.txt index eab350f3..a13ad986 100644 --- a/test/cpp-utils/CMakeLists.txt +++ b/test/cpp-utils/CMakeLists.txt @@ -71,7 +71,7 @@ target_activate_cpp14(${PROJECT_NAME}_exit_signal) target_link_libraries(${PROJECT_NAME}_exit_signal cpp-utils) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest cpp-utils) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest cpp-utils) add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_exit_status ${PROJECT_NAME}_exit_signal) add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/test/cpp-utils/assert/backtrace_test.cpp b/test/cpp-utils/assert/backtrace_test.cpp index 769ae8a2..a5b46709 100644 --- a/test/cpp-utils/assert/backtrace_test.cpp +++ b/test/cpp-utils/assert/backtrace_test.cpp @@ -2,33 +2,30 @@ #include #include "cpp-utils/assert/backtrace.h" #include "cpp-utils/process/subprocess.h" +#include +#include "my-gtest-main.h" using std::string; using testing::HasSubstr; +namespace bf = boost::filesystem; namespace { std::string call_process_exiting_with(const std::string& kind, const std::string& signal = "") { #if defined(_MSC_VER) - constexpr const char* executable = "cpp-utils-test_exit_signal.exe"; + auto executable = get_executable().parent_path() / "cpp-utils-test_exit_signal.exe"; #else - constexpr const char* executable = "./test/cpp-utils/cpp-utils-test_exit_signal"; + auto executable = get_executable().parent_path() / "cpp-utils-test_exit_signal"; #endif - const std::string command = std::string(executable) + " \"" + kind + "\" \"" + signal + "\" 2>&1"; + if (!bf::exists(executable)) { + throw std::runtime_error(executable.string() + " not found."); + } + const std::string command = executable.string() + " \"" + kind + "\" \"" + signal + "\" 2>&1"; auto result = cpputils::Subprocess::call(command); return result.output; } } -TEST(BacktraceTest, ContainsBacktrace) { - string backtrace = cpputils::backtrace(); - EXPECT_THAT(backtrace, HasSubstr("#1")); -} - #if !(defined(_MSC_VER) && defined(NDEBUG)) -TEST(BacktraceTest, ContainsExecutableName) { - string backtrace = cpputils::backtrace(); - EXPECT_THAT(backtrace, HasSubstr("cpp-utils-test")); -} TEST(BacktraceTest, ContainsTopLevelLine) { string backtrace = cpputils::backtrace(); @@ -84,26 +81,55 @@ TEST(BacktraceTest, DoesntCrashOnCaughtException) { } #if !(defined(_MSC_VER) && defined(NDEBUG)) +TEST(BacktraceTest, ContainsBacktrace) { + string backtrace = cpputils::backtrace(); +#if defined(_MSC_VER) + EXPECT_THAT(backtrace, HasSubstr("testing::Test::Run")); +#else + EXPECT_THAT(backtrace, HasSubstr("BacktraceTest_ContainsBacktrace_Test::TestBody")); +#endif +} + TEST(BacktraceTest, ShowBacktraceOnNullptrAccess) { auto output = call_process_exiting_with_nullptr_violation(); - EXPECT_THAT(output, HasSubstr("cpp-utils-test_exit_signal")); +#if defined(_MSC_VER) + EXPECT_THAT(output, HasSubstr("handle_exit_signal")); +#else + EXPECT_THAT(output, HasSubstr("cpputils::backtrace")); +#endif } TEST(BacktraceTest, ShowBacktraceOnSigSegv) { auto output = call_process_exiting_with_sigsegv(); - EXPECT_THAT(output, HasSubstr("cpp-utils-test_exit_signal")); +#if defined(_MSC_VER) + EXPECT_THAT(output, HasSubstr("handle_exit_signal")); +#else + EXPECT_THAT(output, HasSubstr("cpputils::backtrace")); +#endif } TEST(BacktraceTest, ShowBacktraceOnUnhandledException) { auto output = call_process_exiting_with_exception("my_exception_message"); - EXPECT_THAT(output, HasSubstr("cpp-utils-test_exit_signal")); +#if defined(_MSC_VER) + EXPECT_THAT(output, HasSubstr("handle_exit_signal")); +#else + EXPECT_THAT(output, HasSubstr("cpputils::backtrace")); +#endif } TEST(BacktraceTest, ShowBacktraceOnSigIll) { auto output = call_process_exiting_with_sigill(); - EXPECT_THAT(output, HasSubstr("cpp-utils-test_exit_signal")); +#if defined(_MSC_VER) + EXPECT_THAT(output, HasSubstr("handle_exit_signal")); +#else + EXPECT_THAT(output, HasSubstr("cpputils::backtrace")); +#endif } #else +TEST(BacktraceTest, ContainsBacktrace) { + string backtrace = cpputils::backtrace(); + EXPECT_THAT(backtrace, HasSubstr("#1")); +} TEST(BacktraceTest, ShowBacktraceOnNullptrAccess) { auto output = call_process_exiting_with_nullptr_violation(); EXPECT_THAT(output, HasSubstr("#1")); @@ -128,7 +154,7 @@ TEST(BacktraceTest, ShowBacktraceOnSigIll) { #if !defined(_MSC_VER) TEST(BacktraceTest, ShowBacktraceOnSigAbrt) { auto output = call_process_exiting_with_sigabrt(); - EXPECT_THAT(output, HasSubstr("cpp-utils-test_exit_signal")); + EXPECT_THAT(output, HasSubstr("cpputils::backtrace")); } TEST(BacktraceTest, ShowBacktraceOnSigAbrt_ShowsCorrectSignalName) { diff --git a/test/cpp-utils/process/SubprocessTest.cpp b/test/cpp-utils/process/SubprocessTest.cpp index 9a1c857c..c9ed0a83 100644 --- a/test/cpp-utils/process/SubprocessTest.cpp +++ b/test/cpp-utils/process/SubprocessTest.cpp @@ -1,19 +1,26 @@ #include #include +#include #include +#include "my-gtest-main.h" using cpputils::Subprocess; using cpputils::SubprocessError; +using std::string; +namespace bf = boost::filesystem; 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"; + auto executable = get_executable().parent_path() / "cpp-utils-test_exit_status.exe"; #else - constexpr const char* executable = "./test/cpp-utils/cpp-utils-test_exit_status"; + auto executable = get_executable().parent_path() / "cpp-utils-test_exit_status"; #endif - return std::string(executable) + " \"" + message + "\" " + std::to_string(status); + if (!bf::exists(executable)) { + throw std::runtime_error(executable.string() + " not found."); + } + return executable.string() + " \"" + message + "\" " + std::to_string(status); } } diff --git a/test/cpp-utils/thread/debugging_test.cpp b/test/cpp-utils/thread/debugging_test.cpp index ec4f1e3b..caf5f458 100644 --- a/test/cpp-utils/thread/debugging_test.cpp +++ b/test/cpp-utils/thread/debugging_test.cpp @@ -11,6 +11,24 @@ TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenSettingAndGetting_thenD get_thread_name(); } +TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenGettingFromInside_thenIsCorrect) { + set_thread_name("my_thread_name"); + string name = get_thread_name(); + EXPECT_EQ("my_thread_name", name); +} + +TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromInside_thenIsCorrect) { + std::thread child([] { + set_thread_name("my_thread_name"); + string name = get_thread_name(); + EXPECT_EQ("my_thread_name", name); + }); + child.join(); +} + + +#if defined(__GLIBC__) || defined(__APPLE__) || defined(_MSC_VER) +// disabled on musl because getting the thread name for a child thread doesn't work there TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_thenDoesntCrash) { ConditionBarrier nameIsChecked; @@ -27,37 +45,22 @@ TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_then EXPECT_TRUE(child_didnt_crash); } -TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenGettingFromInside_thenIsCorrect) { - set_thread_name("my_thread_name"); - string name = get_thread_name(); - EXPECT_EQ("my_thread_name", name); -} - -TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromInside_thenIsCorrect) { - std::thread child([] { - set_thread_name("my_thread_name"); - string name = get_thread_name(); - EXPECT_EQ("my_thread_name", name); - }); - child.join(); -} - TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromOutside_thenIsCorrect) { - ConditionBarrier nameIsSet; - ConditionBarrier nameIsChecked; + ConditionBarrier nameIsSet; + ConditionBarrier nameIsChecked; - std::thread child([&] { - set_thread_name("my_thread_name"); - nameIsSet.release(); - nameIsChecked.wait(); - }); + std::thread child([&] { + set_thread_name("my_thread_name"); + nameIsSet.release(); + nameIsChecked.wait(); + }); - nameIsSet.wait(); - set_thread_name("outer_thread_name"); // just to make sure the next line doesn't read the outer thread name - string name = get_thread_name(&child); - EXPECT_EQ("my_thread_name", name); + nameIsSet.wait(); + set_thread_name("outer_thread_name"); // just to make sure the next line doesn't read the outer thread name + string name = get_thread_name(&child); + EXPECT_EQ("my_thread_name", name); - nameIsChecked.release(); - child.join(); + nameIsChecked.release(); + child.join(); } - +#endif diff --git a/test/cryfs-cli/CMakeLists.txt b/test/cryfs-cli/CMakeLists.txt index b120419a..2d0b38c5 100644 --- a/test/cryfs-cli/CMakeLists.txt +++ b/test/cryfs-cli/CMakeLists.txt @@ -16,7 +16,7 @@ set(SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest cryfs-cli cryfs-unmount fspp-fuse) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest cryfs-cli cryfs-unmount fspp-fuse) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/test/cryfs/CMakeLists.txt b/test/cryfs/CMakeLists.txt index d4caa9a1..eb13b273 100644 --- a/test/cryfs/CMakeLists.txt +++ b/test/cryfs/CMakeLists.txt @@ -24,7 +24,7 @@ set(SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest cryfs) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest cryfs) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/test/fspp/CMakeLists.txt b/test/fspp/CMakeLists.txt index 8f94cf2b..dabff1f1 100644 --- a/test/fspp/CMakeLists.txt +++ b/test/fspp/CMakeLists.txt @@ -102,7 +102,7 @@ set(SOURCES testutils/OpenFileHandle.cpp testutils/OpenFileHandle.h) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest fspp-interface fspp-fuse) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest fspp-interface fspp-fuse) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/test/fspp/fuse/flush/FuseFlushErrorTest.cpp b/test/fspp/fuse/flush/FuseFlushErrorTest.cpp index d9e5364f..56a0e930 100644 --- a/test/fspp/fuse/flush/FuseFlushErrorTest.cpp +++ b/test/fspp/fuse/flush/FuseFlushErrorTest.cpp @@ -14,7 +14,13 @@ using fspp::fuse::FuseErrnoException; class FuseFlushErrorTest: public FuseFlushTest, public WithParamInterface { }; -INSTANTIATE_TEST_CASE_P(FuseFlushErrorTest, FuseFlushErrorTest, Values(EBADF, EINTR, EIO)); +INSTANTIATE_TEST_CASE_P(FuseFlushErrorTest, FuseFlushErrorTest, Values( + EBADF, +#if defined(__GLIBC__) || defined(__APPLE__) + // musl has different handling for EINTR, see https://ewontfix.com/4/ + EINTR, +#endif + EIO)); TEST_P(FuseFlushErrorTest, ReturnErrorFromFlush) { ReturnIsFileOnLstat(FILENAME); diff --git a/test/fspp/testutils/FuseThread.cpp b/test/fspp/testutils/FuseThread.cpp index 8da58591..31053b10 100644 --- a/test/fspp/testutils/FuseThread.cpp +++ b/test/fspp/testutils/FuseThread.cpp @@ -29,9 +29,7 @@ void FuseThread::start(const bf::path &mountDir, const vector &fuseOptio } void FuseThread::stop() { - if (0 != pthread_kill(_child.native_handle(), SIGINT)) { - throw std::runtime_error("Error sending stop signal"); - } + _fuse->stop(); bool thread_stopped = _child.try_join_for(seconds(10)); ASSERT(thread_stopped, "FuseThread could not be stopped"); //Wait until it is properly shutdown (busy waiting is simple and doesn't hurt much here) diff --git a/test/gitversion/CMakeLists.txt b/test/gitversion/CMakeLists.txt index ea3221a6..51a5ccc1 100644 --- a/test/gitversion/CMakeLists.txt +++ b/test/gitversion/CMakeLists.txt @@ -6,7 +6,7 @@ set(SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest gitversion) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest gitversion) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/test/my-gtest-main/CMakeLists.txt b/test/my-gtest-main/CMakeLists.txt new file mode 100644 index 00000000..1d1e7e08 --- /dev/null +++ b/test/my-gtest-main/CMakeLists.txt @@ -0,0 +1,13 @@ +project (my-gtest-main) + +set(SOURCES + my-gtest-main.cpp +) + +add_library(${PROJECT_NAME} STATIC ${SOURCES}) +target_link_libraries(${PROJECT_NAME} PUBLIC googletest cpp-utils) +target_add_boost(${PROJECT_NAME} filesystem system) +target_include_directories(${PROJECT_NAME} PUBLIC .) + +target_enable_style_warnings(${PROJECT_NAME}) +target_activate_cpp14(${PROJECT_NAME}) diff --git a/test/my-gtest-main/my-gtest-main.cpp b/test/my-gtest-main/my-gtest-main.cpp new file mode 100644 index 00000000..c526e1a4 --- /dev/null +++ b/test/my-gtest-main/my-gtest-main.cpp @@ -0,0 +1,27 @@ +#include "my-gtest-main.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include + +namespace { + boost::optional executable; +} + +const boost::filesystem::path& get_executable() { + ASSERT(executable != boost::none, "Executable path not set"); + return *executable; +} + + +int main(int argc, char** argv) { + executable = boost::filesystem::path(argv[0]); + + // Since Google Mock depends on Google Test, InitGoogleMock() is + // also responsible for initializing Google Test. Therefore there's + // no need for calling testing::InitGoogleTest() separately. + testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/my-gtest-main/my-gtest-main.h b/test/my-gtest-main/my-gtest-main.h new file mode 100644 index 00000000..d77b379f --- /dev/null +++ b/test/my-gtest-main/my-gtest-main.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +const boost::filesystem::path& get_executable(); diff --git a/test/parallelaccessstore/CMakeLists.txt b/test/parallelaccessstore/CMakeLists.txt index 0ec0b5e2..16170d17 100644 --- a/test/parallelaccessstore/CMakeLists.txt +++ b/test/parallelaccessstore/CMakeLists.txt @@ -6,7 +6,7 @@ set(SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} googletest parallelaccessstore) +target_link_libraries(${PROJECT_NAME} my-gtest-main googletest parallelaccessstore) add_test(${PROJECT_NAME} ${PROJECT_NAME}) target_enable_style_warnings(${PROJECT_NAME}) diff --git a/vendor/googletest/CMakeLists.txt b/vendor/googletest/CMakeLists.txt index 2b4b5d24..6c7e98ae 100644 --- a/vendor/googletest/CMakeLists.txt +++ b/vendor/googletest/CMakeLists.txt @@ -10,7 +10,6 @@ if (BUILD_TESTING) project (googletest) add_library(${PROJECT_NAME} dummy.cpp) target_link_libraries(${PROJECT_NAME} PUBLIC gtest gmock) - target_link_libraries(${PROJECT_NAME} PRIVATE gmock_main) target_include_directories(${PROJECT_NAME} SYSTEM INTERFACE ${gtest_INCLUDE_DIRS}/include SYSTEM ${gmock_INCLUDE_DIRS}/include) # Disable "missing override" warning because gmock MOCK_METHOD() don't use override :(