From 844ab58642bb2e619603d72b1a21c9e478e57424 Mon Sep 17 00:00:00 2001 From: Corentin Schreiber Date: Thu, 13 Apr 2023 08:57:15 +0100 Subject: [PATCH] Fix #83 --- src/snitch.cpp | 48 ++++++++++++++++++++++++++------- tests/runtime_tests/section.cpp | 42 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/snitch.cpp b/src/snitch.cpp index e8c54027..310bbab1 100644 --- a/src/snitch.cpp +++ b/src/snitch.cpp @@ -379,11 +379,27 @@ small_function assertion_failed_handler = &terminate_wit namespace snitch::impl { section_entry_checker::~section_entry_checker() { if (entered) { - if (state.sections.levels.size() == state.sections.depth) { + if (state.sections.depth == state.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; } else { - auto& child = state.sections.levels[state.sections.depth]; - if (child.previous_section_id == child.max_section_id) { + // 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]; + if (child.previous_section_id != child.max_section_id) { + no_child_section_left = false; + break; + } + } + + if (no_child_section_left) { + // No more children, we can pop this level and never go back. state.sections.levels.pop_back(); } } @@ -413,14 +429,22 @@ section_entry_checker::operator bool() { auto& level = state.sections.levels[state.sections.depth - 1]; ++level.current_section_id; - if (level.max_section_id < 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 && - (level.previous_section_id + 1 == level.current_section_id || - (level.previous_section_id == level.current_section_id && - state.sections.levels.size() > state.sections.depth))) { + if (state.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; + } + + // Only enter this section if: + // - The section entered in the previous run was its immediate previous sibling, or + // - 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())) { level.previous_section_id = level.current_section_id; state.sections.current_section.push_back(section); @@ -928,6 +952,7 @@ test_state registry::run(test_case& test) noexcept { test.state = impl::test_case_state::success; + // Fetch special tags for this test case. bool may_fail = false; bool should_fail = false; for_each_tag(test.id.tags, [&](const tags::parsed_tag& v) { @@ -951,12 +976,13 @@ test_state registry::run(test_case& test) noexcept { #endif 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.sections.leaf_executed = false; - + // Run the test case. #if SNITCH_WITH_EXCEPTIONS try { test.func(); @@ -974,8 +1000,10 @@ test_state registry::run(test_case& test) noexcept { #endif if (state.sections.levels.size() == 1) { + // This test case contained sections; check if there are any more left to evaluate. auto& child = state.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(); } diff --git a/tests/runtime_tests/section.cpp b/tests/runtime_tests/section.cpp index 0f2437a3..5ad63c9b 100644 --- a/tests/runtime_tests/section.cpp +++ b/tests/runtime_tests/section.cpp @@ -170,6 +170,48 @@ TEST_CASE("section", "[test macros]") { CHECK_CASE(snitch::test_case_state::failed, 11u); } + SECTION("nested sections multiple leaves") { + framework.test_case.func = []() { + SNITCH_SECTION("section 1") { + SNITCH_SECTION("section 1.1") { + SNITCH_SECTION("section 1.1.1") { + SNITCH_FAIL_CHECK("trigger"); + } + SNITCH_SECTION("section 1.1.2") { + SNITCH_FAIL_CHECK("trigger"); + } + SNITCH_SECTION("section 1.1.3") { + SNITCH_FAIL_CHECK("trigger"); + } + } + } + SNITCH_SECTION("section 2") { + SNITCH_SECTION("section 2.1") { + SNITCH_SECTION("section 2.1.1") { + SNITCH_FAIL_CHECK("trigger"); + } + SNITCH_SECTION("section 2.1.2") { + SNITCH_FAIL_CHECK("trigger"); + } + SNITCH_SECTION("section 2.1.3") { + SNITCH_FAIL_CHECK("trigger"); + } + } + } + }; + + framework.run_test(); + + REQUIRE(framework.get_num_failures() == 6u); + CHECK_SECTIONS_FOR_FAILURE(0u, "section 1", "section 1.1", "section 1.1.1"); + CHECK_SECTIONS_FOR_FAILURE(1u, "section 1", "section 1.1", "section 1.1.2"); + CHECK_SECTIONS_FOR_FAILURE(2u, "section 1", "section 1.1", "section 1.1.3"); + CHECK_SECTIONS_FOR_FAILURE(3u, "section 2", "section 2.1", "section 2.1.1"); + CHECK_SECTIONS_FOR_FAILURE(4u, "section 2", "section 2.1", "section 2.1.2"); + CHECK_SECTIONS_FOR_FAILURE(5u, "section 2", "section 2.1", "section 2.1.3"); + CHECK_CASE(snitch::test_case_state::failed, 6u); + } + SECTION("one section in a loop") { framework.test_case.func = []() { for (std::size_t i = 0u; i < 5u; ++i) {