Skip to content

Commit

Permalink
Port the fix for #177 to SECTION as well
Browse files Browse the repository at this point in the history
  • Loading branch information
cschreib committed Aug 28, 2024
1 parent 98a410e commit 7994241
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 78 deletions.
17 changes: 3 additions & 14 deletions include/snitch/snitch_capture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@

namespace snitch::impl {
struct scoped_capture {
capture_state& captures;
#if SNITCH_WITH_EXCEPTIONS
std::optional<capture_state>& held_captures;
#endif
test_state& state;
std::size_t count = 0;

SNITCH_EXPORT ~scoped_capture();
Expand All @@ -38,23 +35,15 @@ 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
return {state, sizeof...(args)};
}

// 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
return {state, 1};
}
} // namespace snitch::impl

Expand Down
13 changes: 9 additions & 4 deletions include/snitch/snitch_test_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,15 +279,20 @@ using capture_state = small_vector<small_string<max_capture_length>, max_capture
// NB: +2 is because we need one for the test case location, and one for the check location
using location_state = small_vector<assertion_location, max_nested_sections + 2>;

struct test_state {
registry& reg;
test_case& test;
struct info_state {
section_state sections = {};
capture_state captures = {};
location_state locations = {};
};

struct test_state {
registry& reg;
test_case& test;

info_state info = {};

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

std::size_t asserts = 0;
Expand Down
16 changes: 8 additions & 8 deletions src/snitch_capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ void trim(std::string_view& str, std::string_view patterns) noexcept {

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

captures.resize(captures.size() - count);
state.info.captures.resize(state.info.captures.size() - count);
}

std::string_view extract_next_name(std::string_view& names) noexcept {
Expand Down Expand Up @@ -82,7 +82,7 @@ std::string_view extract_next_name(std::string_view& names) noexcept {
}

small_string<max_capture_length>& add_capture(test_state& state) {
if (state.captures.available() == 0) {
if (state.info.captures.available() == 0) {
state.reg.print(
make_colored("error:", state.reg.with_color, color::fail),
" max number of captures reached; "
Expand All @@ -92,11 +92,11 @@ small_string<max_capture_length>& add_capture(test_state& state) {
}

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

state.captures.grow(1);
state.captures.back().clear();
return state.captures.back();
state.info.captures.grow(1);
state.info.captures.back().clear();
return state.info.captures.back();
}
} // namespace snitch::impl
56 changes: 29 additions & 27 deletions src/snitch_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,39 +413,41 @@ void report_assertion_impl(

register_assertion(success, state);

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 bool use_held_info = state.unhandled_exception && state.held_info.has_value();

const auto captures_buffer = impl::make_capture_buffer(
use_held_info ? state.held_info.value().captures : state.info.captures);

const auto& current_section = use_held_info ? state.held_info.value().sections.current_section
: state.info.sections.current_section;

const auto& last_location =
use_held_info ? state.held_info.value().locations.back() : state.info.locations.back();

const auto& last_location = state.locations.back();
#if SNITCH_WITH_EXCEPTIONS
const auto location =
state.in_check
? assertion_location{last_location.file, last_location.line, location_type::exact}
: last_location;
#else
const auto location =
const auto captures_buffer = impl::make_capture_buffer(state.info.captures);
const auto& current_section = state.info.sections.current_section;
const auto& last_location = state.info.locations.back();
const auto location =
assertion_location{last_location.file, last_location.line, location_type::exact};
#endif

if (success) {
if (r.verbose >= registry::verbosity::full) {
r.report_callback(
r, event::assertion_succeeded{
state.test.id, state.sections.current_section, captures_buffer.span(),
location, data});
state.test.id, current_section, captures_buffer.span(), location, data});
}
} else {
r.report_callback(
r, event::assertion_failed{
state.test.id, state.sections.current_section, captures_buffer.span(), location,
data, state.should_fail, state.may_fail});
state.test.id, current_section, captures_buffer.span(), location, data,
state.should_fail, state.may_fail});
}
}
} // namespace
Expand Down Expand Up @@ -482,13 +484,13 @@ void registry::report_skipped(std::string_view message) noexcept {
impl::test_state& state = impl::get_current_test();
impl::set_state(state.test, impl::test_case_state::skipped);

const auto captures_buffer = impl::make_capture_buffer(state.captures);
const auto& location = state.locations.back();
const auto captures_buffer = impl::make_capture_buffer(state.info.captures);
const auto& location = state.info.locations.back();

state.reg.report_callback(
state.reg, event::test_case_skipped{
state.test.id,
state.sections.current_section,
state.info.sections.current_section,
captures_buffer.span(),
{location.file, location.line, location_type::exact},
message});
Expand All @@ -515,7 +517,7 @@ impl::test_state registry::run(impl::test_case& test) noexcept {
impl::test_state state{
.reg = *this, .test = test, .may_fail = may_fail, .should_fail = should_fail};

state.locations.push_back(
state.info.locations.push_back(
{test.location.file, test.location.line, location_type::test_case_scope});

// Store previously running test, to restore it later.
Expand All @@ -534,24 +536,24 @@ impl::test_state registry::run(impl::test_case& test) noexcept {

do {
// Reset section state.
state.sections.leaf_executed = false;
for (std::size_t i = 0; i < state.sections.levels.size(); ++i) {
state.sections.levels[i].current_section_id = 0;
state.info.sections.leaf_executed = false;
for (std::size_t i = 0; i < state.info.sections.levels.size(); ++i) {
state.info.sections.levels[i].current_section_id = 0;
}

// Run the test case.
test.func();

if (state.sections.levels.size() == 1) {
if (state.info.sections.levels.size() == 1) {
// This test case contained sections; check if there are any more left to evaluate.
auto& child = state.sections.levels[0];
auto& child = state.info.sections.levels[0];
if (child.previous_section_id == child.max_section_id) {
// No more; clear the section state.
state.sections.levels.clear();
state.sections.current_section.clear();
state.info.sections.levels.clear();
state.info.sections.current_section.clear();
}
}
} while (!state.sections.levels.empty() &&
} while (!state.info.sections.levels.empty() &&
state.test.state != impl::test_case_state::skipped);

#if SNITCH_WITH_EXCEPTIONS
Expand Down
46 changes: 27 additions & 19 deletions src/snitch_section.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,33 @@

namespace snitch::impl {
section_entry_checker::~section_entry_checker() {
auto& sections = state.info.sections;

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

pop_location(state);

if (state.sections.depth == state.sections.levels.size()) {
if (sections.depth == sections.levels.size()) {
// We just entered this section, and there was no child section in it.
// This is a leaf; flag that a leaf has been executed so that no other leaf
// is executed in this run.
// Note: don't pop this level from the section state yet, it may have siblings
// that we don't know about yet. Popping will be done when we exit from the parent,
// since then we will know if there is any sibling.
state.sections.leaf_executed = true;
sections.leaf_executed = true;
} else {
// Check if there is any child section left to execute, at any depth below this one.
bool no_child_section_left = true;
for (std::size_t c = state.sections.depth; c < state.sections.levels.size(); ++c) {
auto& child = state.sections.levels[c];
for (std::size_t c = sections.depth; c < sections.levels.size(); ++c) {
auto& child = sections.levels[c];
if (child.previous_section_id != child.max_section_id) {
no_child_section_left = false;
break;
Expand All @@ -42,19 +44,25 @@ section_entry_checker::~section_entry_checker() {

if (no_child_section_left) {
// No more children, we can pop this level and never go back.
state.sections.levels.pop_back();
sections.levels.pop_back();
}
}

state.sections.current_section.pop_back();
sections.current_section.pop_back();
}

--state.sections.depth;
--sections.depth;
}

section_entry_checker::operator bool() {
if (state.sections.depth >= state.sections.levels.size()) {
if (state.sections.depth >= max_nested_sections) {
#if SNITCH_WITH_EXCEPTIONS
state.held_info.reset();
#endif

auto& sections = state.info.sections;

if (sections.depth >= sections.levels.size()) {
if (sections.depth >= max_nested_sections) {
using namespace snitch::impl;
state.reg.print(
make_colored("error:", state.reg.with_color, color::fail),
Expand All @@ -64,19 +72,19 @@ section_entry_checker::operator bool() {
assertion_failed("max number of nested sections reached");
}

state.sections.levels.push_back({});
sections.levels.push_back({});
}

++state.sections.depth;
++sections.depth;

auto& level = state.sections.levels[state.sections.depth - 1];
auto& level = sections.levels[sections.depth - 1];

++level.current_section_id;
if (level.current_section_id > level.max_section_id) {
level.max_section_id = level.current_section_id;
}

if (state.sections.leaf_executed) {
if (sections.leaf_executed) {
// We have already executed another leaf section; can't execute more
// on this run, so don't bother going inside this one now.
return false;
Expand All @@ -87,10 +95,10 @@ section_entry_checker::operator bool() {
// - This section was already entered in the previous run, and child sections exist in it.
if (level.current_section_id == level.previous_section_id + 1 ||
(level.current_section_id == level.previous_section_id &&
state.sections.depth < state.sections.levels.size())) {
sections.depth < sections.levels.size())) {

level.previous_section_id = level.current_section_id;
state.sections.current_section.push_back(data);
sections.current_section.push_back(data);
push_location(
state, {data.location.file, data.location.line, location_type::section_scope});
entered = true;
Expand Down
16 changes: 10 additions & 6 deletions src/snitch_test_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ void set_current_test(test_state* current) noexcept {
}

void push_location(test_state& test, const assertion_location& location) noexcept {
test.locations.push_back(location);
test.info.locations.push_back(location);
}

void pop_location(test_state& test) noexcept {
test.locations.pop_back();
test.info.locations.pop_back();
}

scoped_test_check::scoped_test_check(const source_location& location) noexcept :
test(get_current_test()) {
#if SNITCH_WITH_EXCEPTIONS
test.held_info.reset();
#endif

push_location(test, {location.file, location.line, location_type::in_check});
test.in_check = true;
}
Expand All @@ -44,11 +48,11 @@ scoped_test_check::~scoped_test_check() noexcept {
test.in_check = false;

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

Expand Down
Loading

0 comments on commit 7994241

Please sign in to comment.