Skip to content

Instantly share code, notes, and snippets.

@m-pilia
Last active June 9, 2022 20:57
Show Gist options
  • Save m-pilia/6cb686b0fe0ab22a4f6feebdd177bdf8 to your computer and use it in GitHub Desktop.
Save m-pilia/6cb686b0fe0ab22a4f6feebdd177bdf8 to your computer and use it in GitHub Desktop.
Catch2 death test (fork)
#include <iostream>
#include <catch2/catch_test_macros.hpp>
#include "abnormal_termination.hpp"
TEST_CASE("Example test", "[example]" ) {
CHECK(abnormalTermination([]() {
std::cout << "hallo\n";
std::abort();
}));
}
#include <functional>
#include <unistd.h>
#include <sys/wait.h>
#include "default_signal_handling_guard.hpp"
/**
* Execute a callable in a child process, and retur true if execution of
* the callbale terminates abnormally due to a signal being raised.
*
* NOTE: This function ignores signal handlers set before its call.
*/
bool abnormalTermination(const std::function<void(void)>& callable) {
constexpr auto forkFailure = -1;
constexpr auto childProcess = 0;
int exitStatus{};
switch (fork()) {
case forkFailure:
throw std::runtime_error("Failed to spawn child process");
case childProcess:
{
DefaultSignalHandlingGuard guard{};
callable();
}
std::exit(EXIT_SUCCESS);
default:
break;
}
wait(&exitStatus);
return WIFSIGNALED(exitStatus) != 0;
}
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 17)
project(catch2_death_test_example LANGUAGES CXX VERSION 0.0.1)
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.0.1
)
FetchContent_MakeAvailable(Catch2)
add_executable(example_test_suite _death_test.cpp)
target_link_libraries(example_test_suite PRIVATE Catch2::Catch2WithMain)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
catch_discover_tests(example_test_suite)
#include <array>
#include <csignal>
#include <iostream>
#include <map>
/**
* Restore default signal handlers for the standard signals under the lifetime of the guard.
*/
class DefaultSignalHandlingGuard {
public:
DefaultSignalHandlingGuard() {
for (const auto signalId : _signalIds) {
const auto previousHandler = std::signal(signalId, SIG_DFL);
if (previousHandler == SIG_ERR) {
throw std::runtime_error("Failed to set signal handler for signal id " + std::to_string(signalId));
}
_previousHandlers[signalId] = previousHandler;
}
}
~DefaultSignalHandlingGuard() {
for (const auto [signalId, previousHandler] : _previousHandlers) {
const auto result = std::signal(signalId, previousHandler);
if (result == SIG_ERR) {
std::cerr << "Failed to restore signal handler for signal id " << signalId;
}
}
}
private:
using SignalId = decltype(SIGINT);
using SignalHandler = void(*)(SignalId);
static constexpr std::array _signalIds{
SIGINT,
SIGILL,
SIGFPE,
SIGSEGV,
SIGTERM,
SIGABRT,
};
std::map<SignalId, SignalHandler> _previousHandlers{};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment