Skip to content

Commit

Permalink
Better captures on handled exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
cschreib committed Aug 22, 2024
1 parent 3661ec0 commit f47651b
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 6 deletions.
13 changes: 12 additions & 1 deletion include/snitch/snitch_capture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
namespace snitch::impl {
struct scoped_capture {
capture_state& captures;
std::size_t count = 0;
#if SNITCH_WITH_EXCEPTIONS
std::optional<capture_state>& held_captures;
#endif
std::size_t count = 0;

SNITCH_EXPORT ~scoped_capture();
};
Expand All @@ -35,15 +38,23 @@ void add_capture(test_state& state, std::string_view& names, const T& arg) {
template<string_appendable... Args>
scoped_capture add_captures(test_state& state, std::string_view names, const Args&... args) {
(add_capture(state, names, args), ...);
#if SNITCH_WITH_EXCEPTIONS
return {state.captures, state.held_captures, sizeof...(args)};
#else
return {state.captures, sizeof...(args)};
#endif
}

// Requires: number of captures < max_captures.
template<string_appendable... Args>
scoped_capture add_info(test_state& state, const Args&... args) {
auto& capture = add_capture(state);
append_or_truncate(capture, args...);
#if SNITCH_WITH_EXCEPTIONS
return {state.captures, state.held_captures, 1};
#else
return {state.captures, 1};
#endif
}
} // namespace snitch::impl

Expand Down
9 changes: 9 additions & 0 deletions include/snitch/snitch_test_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "snitch/snitch_vector.hpp"

#include <cstddef>
#include <optional>
#include <string_view>

namespace snitch {
Expand Down Expand Up @@ -285,13 +286,21 @@ struct test_state {
capture_state captures = {};
location_state locations = {};

#if SNITCH_WITH_EXCEPTIONS
std::optional<capture_state> held_captures = {};
#endif

std::size_t asserts = 0;
std::size_t failures = 0;
std::size_t allowed_failures = 0;
bool may_fail = false;
bool should_fail = false;
bool in_check = false;

#if SNITCH_WITH_EXCEPTIONS
bool unhandled_exception = false;
#endif

#if SNITCH_WITH_TIMINGS
float duration = 0.0f;
#endif
Expand Down
10 changes: 7 additions & 3 deletions src/snitch_capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ void trim(std::string_view& str, std::string_view patterns) noexcept {

scoped_capture::~scoped_capture() {
#if SNITCH_WITH_EXCEPTIONS
if (std::uncaught_exceptions() > 0) {
if (std::uncaught_exceptions() > 0 && !held_captures.has_value()) {
// We are unwinding the stack because an exception has been thrown;
// avoid touching the capture state since we will want to preserve the information
// keep a copy of the full capture state since we will want to preserve the information
// when reporting the exception.
return;
held_captures = captures;
}
#endif

Expand Down Expand Up @@ -91,6 +91,10 @@ small_string<max_capture_length>& add_capture(test_state& state) {
assertion_failed("max number of captures reached");
}

#if SNITCH_WITH_EXCEPTIONS
state.held_captures.reset();
#endif

state.captures.grow(1);
state.captures.back().clear();
return state.captures.back();
Expand Down
15 changes: 13 additions & 2 deletions src/snitch_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,17 @@ void report_assertion_impl(

register_assertion(success, state);

const auto captures_buffer = impl::make_capture_buffer(state.captures);
const auto& last_location = state.locations.back();
const auto captures_buffer =
#if SNITCH_WITH_EXCEPTIONS
impl::make_capture_buffer(
state.unhandled_exception && state.held_captures.has_value()
? state.held_captures.value()
: state.captures);
#else
impl::make_capture_buffer(state.captures);
#endif

const auto& last_location = state.locations.back();
#if SNITCH_WITH_EXCEPTIONS
const auto location =
state.in_check
Expand Down Expand Up @@ -552,8 +561,10 @@ impl::test_state registry::run(impl::test_case& test) noexcept {
} catch (const impl::abort_exception&) {
// Test aborted, assume its state was already set accordingly.
} catch (const std::exception& e) {
state.unhandled_exception = true;
report_assertion(false, "unexpected std::exception caught; message: ", e.what());
} catch (...) {
state.unhandled_exception = true;
report_assertion(false, "unexpected unknown exception caught");
}
#endif
Expand Down
95 changes: 95 additions & 0 deletions tests/runtime_tests/capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,101 @@ TEST_CASE("capture", "[test macros]") {
REQUIRE(framework.get_num_failures() == 1u);
CHECK_CAPTURES_FOR_FAILURE(0u, "i := 1");
}

SECTION("with handled exception") {
framework.test_case.func = []() {
try {
int i = 1;
SNITCH_CAPTURE(i);
throw std::runtime_error("bad");
} catch (...) {
}

int j = 2;
SNITCH_CAPTURE(j);
SNITCH_CHECK(j == 1);
};

framework.run_test();
CHECK_CAPTURES("j := 2");
}

SECTION("with handled exception no capture") {
framework.test_case.func = []() {
try {
int i = 1;
SNITCH_CAPTURE(i);
throw std::runtime_error("bad");
} catch (...) {
}

int j = 2;
SNITCH_CHECK(j == 1);
};

framework.run_test();
CHECK_NO_CAPTURE;
}

SECTION("with handled exceptions") {
framework.test_case.func = []() {
try {
int i = 1;
SNITCH_CAPTURE(i);
throw std::runtime_error("bad");
} catch (...) {
}

try {
int j = 2;
SNITCH_CAPTURE(j);
throw std::runtime_error("bad");
} catch (...) {
}

int k = 3;
SNITCH_CAPTURE(k);
SNITCH_CHECK(k == 1);
};

framework.run_test();
CHECK_CAPTURES("k := 3");
}

SECTION("with handled exception then unhandled") {
framework.test_case.func = []() {
try {
int i = 1;
SNITCH_CAPTURE(i);
throw std::runtime_error("bad");
} catch (...) {
}

int j = 2;
SNITCH_CAPTURE(j);
throw std::runtime_error("bad");
};

framework.run_test();
CHECK_CAPTURES("j := 2");
}

SECTION("with handled exception then unhandled no capture") {
framework.test_case.func = []() {
try {
int i = 1;
SNITCH_CAPTURE(i);
throw std::runtime_error("bad");
} catch (...) {
}

throw std::runtime_error("bad");
};

framework.run_test();
// FIXME: expected nothing
CHECK_CAPTURES("i := 1");
}
#endif
}

Expand Down

0 comments on commit f47651b

Please sign in to comment.