diff options
author | Stefan <29021710+Saalvage@users.noreply.github.com> | 2022-04-25 12:01:11 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-25 12:01:11 +0300 |
commit | eb8b04a2b1db23ea5665cd8b5cbe859beb0ef80d (patch) | |
tree | c21c5461eeacba80a477bfaa0b0ae3ef494daacf | |
parent | 0b79b49aac67f393aad4b90d578b1a562c7a19e4 (diff) |
Fix subcase reentry (#598)
* Add tests
* Fix #597
* Slightly alternate approach to subcases
* Replace usage of non-standard contains method
* Merge branch 'dev' into fix-subcase-reentry
-rw-r--r-- | doctest/doctest.h | 180 | ||||
-rw-r--r-- | doctest/parts/doctest.cpp | 176 | ||||
-rw-r--r-- | doctest/parts/doctest_fwd.h | 4 | ||||
-rw-r--r-- | examples/all_features/subcases.cpp | 20 | ||||
-rw-r--r-- | examples/all_features/test_output/filter_2.txt | 2 | ||||
-rw-r--r-- | examples/all_features/test_output/filter_2_xml.txt | 3 | ||||
-rw-r--r-- | examples/all_features/test_output/filter_3.txt | 9 | ||||
-rw-r--r-- | examples/all_features/test_output/filter_3_junit.txt | 6 | ||||
-rw-r--r-- | examples/all_features/test_output/filter_3_xml.txt | 10 | ||||
-rw-r--r-- | examples/all_features/test_output/no_multi_lane_atomics.txt | 67 | ||||
-rw-r--r-- | examples/all_features/test_output/no_multithreading.txt | 67 | ||||
-rw-r--r-- | examples/all_features/test_output/subcases.cpp.txt | 67 | ||||
-rw-r--r-- | examples/all_features/test_output/subcases.cpp_junit.txt | 62 | ||||
-rw-r--r-- | examples/all_features/test_output/subcases.cpp_xml.txt | 131 |
14 files changed, 664 insertions, 140 deletions
diff --git a/doctest/doctest.h b/doctest/doctest.h index d576bbd5..efe5877f 100644 --- a/doctest/doctest.h +++ b/doctest/doctest.h @@ -807,6 +807,7 @@ struct DOCTEST_INTERFACE SubcaseSignature const char* m_file; int m_line; + bool operator==(const SubcaseSignature& other) const; bool operator<(const SubcaseSignature& other) const; }; @@ -1230,6 +1231,9 @@ namespace detail { ~Subcase(); operator bool() const; + + private: + bool checkFilters(); }; template <typename L, typename R> @@ -3105,6 +3109,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #endif // DOCTEST_CONFIG_NO_MULTITHREADING #include <set> #include <map> +#include <unordered_set> #include <exception> #include <stdexcept> #include <csignal> @@ -3469,11 +3474,12 @@ typedef timer_large_integer::type ticks_t; std::vector<String> stringifiedContexts; // logging from INFO() due to an exception // stuff for subcases - std::vector<SubcaseSignature> subcasesStack; - std::set<decltype(subcasesStack)> subcasesPassed; - int subcasesCurrentMaxLevel; - bool should_reenter; - Atomic<bool> shouldLogCurrentException; + bool reachedLeaf; + std::vector<SubcaseSignature> subcaseStack; + std::vector<SubcaseSignature> nextSubcaseStack; + std::unordered_set<unsigned long long> fullyTraversedSubcases; + size_t currentSubcaseDepth; + Atomic<bool> shouldLogCurrentException; void resetRunData() { numTestCases = 0; @@ -3810,6 +3816,12 @@ const char* skipPathFromFilename(const char* file) { DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP +bool SubcaseSignature::operator==(const SubcaseSignature& other) const { + return m_line == other.m_line + && std::strcmp(m_file, other.m_file) == 0 + && m_name == other.m_name; +} + bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; @@ -4048,59 +4060,92 @@ namespace { return !*wild; } - //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html - //unsigned hashStr(unsigned const char* str) { - // unsigned long hash = 5381; - // char c; - // while((c = *str++)) - // hash = ((hash << 5) + hash) + c; // hash * 33 + c - // return hash; - //} - // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector<String>& filters, bool matchEmpty, - bool caseSensitive) { - if(filters.empty() && matchEmpty) + bool caseSensitive) { + if (filters.empty() && matchEmpty) return true; - for(auto& curr : filters) - if(wildcmp(name, curr.c_str(), caseSensitive)) + for (auto& curr : filters) + if (wildcmp(name, curr.c_str(), caseSensitive)) return true; return false; } -} // namespace -namespace detail { - Subcase::Subcase(const String& name, const char* file, int line) - : m_signature({name, file, line}) { - auto* s = g_cs; + unsigned long long hash(unsigned long long a, unsigned long long b) { + return (a << 5) + b; + } - // check subcase filters - if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { - if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) - return; - if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) - return; - } - - // if a Subcase on the same level has already been entered - if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { - s->should_reenter = true; - return; - } + // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html + unsigned long long hash(const char* str) { + unsigned long long hash = 5381; + char c; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; // hash * 33 + c + return hash; + } - // push the current signature to the stack so we can check if the - // current stack + the current new subcase have been traversed - s->subcasesStack.push_back(m_signature); - if(s->subcasesPassed.count(s->subcasesStack) != 0) { - // pop - revert to previous stack since we've already passed this - s->subcasesStack.pop_back(); - return; + unsigned long long hash(const SubcaseSignature& sig) { + return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line); + } + + unsigned long long hash(const std::vector<SubcaseSignature>& sigs, size_t count) { + unsigned long long running = 0; + auto end = sigs.begin() + count; + for (auto it = sigs.begin(); it != end; it++) { + running = hash(running, hash(*it)); } + return running; + } - s->subcasesCurrentMaxLevel = s->subcasesStack.size(); - m_entered = true; + unsigned long long hash(const std::vector<SubcaseSignature>& sigs) { + unsigned long long running = 0; + for (const SubcaseSignature& sig : sigs) { + running = hash(running, hash(sig)); + } + return running; + } +} // namespace +namespace detail { + bool Subcase::checkFilters() { + if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) { + if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive)) + return true; + if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive)) + return true; + } + return false; + } - DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + Subcase::Subcase(const String& name, const char* file, int line) + : m_signature({name, file, line}) { + if (!g_cs->reachedLeaf) { + if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size() + || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) { + // Going down. + if (checkFilters()) { return; } + + g_cs->subcaseStack.push_back(m_signature); + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } + } else { + if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) { + // This subcase is reentered via control flow. + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth + && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature))) + == g_cs->fullyTraversedSubcases.end()) { + if (checkFilters()) { return; } + // This subcase is part of the one to be executed next. + g_cs->nextSubcaseStack.clear(); + g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(), + g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth); + g_cs->nextSubcaseStack.push_back(m_signature); + } + } } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 @@ -4108,25 +4153,33 @@ namespace detail { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") Subcase::~Subcase() { - if(m_entered) { - // only mark the subcase stack as passed if no subcases have been skipped - if(g_cs->should_reenter == false) - g_cs->subcasesPassed.insert(g_cs->subcasesStack); - g_cs->subcasesStack.pop_back(); + if (m_entered) { + g_cs->currentSubcaseDepth--; + + if (!g_cs->reachedLeaf) { + // Leaf. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + g_cs->nextSubcaseStack.clear(); + g_cs->reachedLeaf = true; + } else if (g_cs->nextSubcaseStack.empty()) { + // All children are finished. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + } #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0 #else if(std::uncaught_exception() #endif - && g_cs->shouldLogCurrentException) { + && g_cs->shouldLogCurrentException) { DOCTEST_ITERATE_THROUGH_REPORTERS( test_case_exception, {"exception thrown in subcase - will translate later " - "when the whole test case has been exited (cannot " - "translate while there is an active exception)", - false}); + "when the whole test case has been exited (cannot " + "translate while there is an active exception)", + false}); g_cs->shouldLogCurrentException = false; } + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } } @@ -4767,8 +4820,8 @@ namespace { DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); - while(g_cs->subcasesStack.size()) { - g_cs->subcasesStack.pop_back(); + while (g_cs->subcaseStack.size()) { + g_cs->subcaseStack.pop_back(); DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } @@ -6839,7 +6892,7 @@ int Context::run() { p->numAssertsFailedCurrentTest_atomic = 0; p->numAssertsCurrentTest_atomic = 0; - p->subcasesPassed.clear(); + p->fullyTraversedSubcases.clear(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); @@ -6849,9 +6902,10 @@ int Context::run() { do { // reset some of the fields for subcases (except for the set of fully passed ones) - p->should_reenter = false; - p->subcasesCurrentMaxLevel = 0; - p->subcasesStack.clear(); + p->reachedLeaf = false; + // May not be empty if previous subcase exited via exception. + p->subcaseStack.clear(); + p->currentSubcaseDepth = 0; p->shouldLogCurrentException = true; @@ -6885,9 +6939,9 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; } - if(p->should_reenter && run_test) + if(!p->nextSubcaseStack.empty() && run_test) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); - if(!p->should_reenter) + if(p->nextSubcaseStack.empty()) run_test = false; } while(run_test); diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp index bf9e1816..5d27a041 100644 --- a/doctest/parts/doctest.cpp +++ b/doctest/parts/doctest.cpp @@ -88,6 +88,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #endif // DOCTEST_CONFIG_NO_MULTITHREADING #include <set> #include <map> +#include <unordered_set> #include <exception> #include <stdexcept> #include <csignal> @@ -452,11 +453,12 @@ typedef timer_large_integer::type ticks_t; std::vector<String> stringifiedContexts; // logging from INFO() due to an exception // stuff for subcases - std::vector<SubcaseSignature> subcasesStack; - std::set<decltype(subcasesStack)> subcasesPassed; - int subcasesCurrentMaxLevel; - bool should_reenter; - Atomic<bool> shouldLogCurrentException; + bool reachedLeaf; + std::vector<SubcaseSignature> subcaseStack; + std::vector<SubcaseSignature> nextSubcaseStack; + std::unordered_set<unsigned long long> fullyTraversedSubcases; + size_t currentSubcaseDepth; + Atomic<bool> shouldLogCurrentException; void resetRunData() { numTestCases = 0; @@ -793,6 +795,12 @@ const char* skipPathFromFilename(const char* file) { DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP +bool SubcaseSignature::operator==(const SubcaseSignature& other) const { + return m_line == other.m_line + && std::strcmp(m_file, other.m_file) == 0 + && m_name == other.m_name; +} + bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; @@ -1031,59 +1039,92 @@ namespace { return !*wild; } - //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html - //unsigned hashStr(unsigned const char* str) { - // unsigned long hash = 5381; - // char c; - // while((c = *str++)) - // hash = ((hash << 5) + hash) + c; // hash * 33 + c - // return hash; - //} - // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector<String>& filters, bool matchEmpty, - bool caseSensitive) { - if(filters.empty() && matchEmpty) + bool caseSensitive) { + if (filters.empty() && matchEmpty) return true; - for(auto& curr : filters) - if(wildcmp(name, curr.c_str(), caseSensitive)) + for (auto& curr : filters) + if (wildcmp(name, curr.c_str(), caseSensitive)) return true; return false; } -} // namespace -namespace detail { - Subcase::Subcase(const String& name, const char* file, int line) - : m_signature({name, file, line}) { - auto* s = g_cs; + unsigned long long hash(unsigned long long a, unsigned long long b) { + return (a << 5) + b; + } - // check subcase filters - if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { - if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) - return; - if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) - return; - } - - // if a Subcase on the same level has already been entered - if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { - s->should_reenter = true; - return; - } + // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html + unsigned long long hash(const char* str) { + unsigned long long hash = 5381; + char c; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; // hash * 33 + c + return hash; + } - // push the current signature to the stack so we can check if the - // current stack + the current new subcase have been traversed - s->subcasesStack.push_back(m_signature); - if(s->subcasesPassed.count(s->subcasesStack) != 0) { - // pop - revert to previous stack since we've already passed this - s->subcasesStack.pop_back(); - return; + unsigned long long hash(const SubcaseSignature& sig) { + return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line); + } + + unsigned long long hash(const std::vector<SubcaseSignature>& sigs, size_t count) { + unsigned long long running = 0; + auto end = sigs.begin() + count; + for (auto it = sigs.begin(); it != end; it++) { + running = hash(running, hash(*it)); } + return running; + } - s->subcasesCurrentMaxLevel = s->subcasesStack.size(); - m_entered = true; + unsigned long long hash(const std::vector<SubcaseSignature>& sigs) { + unsigned long long running = 0; + for (const SubcaseSignature& sig : sigs) { + running = hash(running, hash(sig)); + } + return running; + } +} // namespace +namespace detail { + bool Subcase::checkFilters() { + if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) { + if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive)) + return true; + if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive)) + return true; + } + return false; + } - DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + Subcase::Subcase(const String& name, const char* file, int line) + : m_signature({name, file, line}) { + if (!g_cs->reachedLeaf) { + if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size() + || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) { + // Going down. + if (checkFilters()) { return; } + + g_cs->subcaseStack.push_back(m_signature); + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } + } else { + if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) { + // This subcase is reentered via control flow. + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth + && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature))) + == g_cs->fullyTraversedSubcases.end()) { + if (checkFilters()) { return; } + // This subcase is part of the one to be executed next. + g_cs->nextSubcaseStack.clear(); + g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(), + g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth); + g_cs->nextSubcaseStack.push_back(m_signature); + } + } } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 @@ -1091,25 +1132,33 @@ namespace detail { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") Subcase::~Subcase() { - if(m_entered) { - // only mark the subcase stack as passed if no subcases have been skipped - if(g_cs->should_reenter == false) - g_cs->subcasesPassed.insert(g_cs->subcasesStack); - g_cs->subcasesStack.pop_back(); + if (m_entered) { + g_cs->currentSubcaseDepth--; + + if (!g_cs->reachedLeaf) { + // Leaf. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + g_cs->nextSubcaseStack.clear(); + g_cs->reachedLeaf = true; + } else if (g_cs->nextSubcaseStack.empty()) { + // All children are finished. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + } #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0 #else if(std::uncaught_exception() #endif - && g_cs->shouldLogCurrentException) { + && g_cs->shouldLogCurrentException) { DOCTEST_ITERATE_THROUGH_REPORTERS( test_case_exception, {"exception thrown in subcase - will translate later " - "when the whole test case has been exited (cannot " - "translate while there is an active exception)", - false}); + "when the whole test case has been exited (cannot " + "translate while there is an active exception)", + false}); g_cs->shouldLogCurrentException = false; } + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } } @@ -1750,8 +1799,8 @@ namespace { DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); - while(g_cs->subcasesStack.size()) { - g_cs->subcasesStack.pop_back(); + while (g_cs->subcaseStack.size()) { + g_cs->subcaseStack.pop_back(); DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } @@ -3822,7 +3871,7 @@ int Context::run() { p->numAssertsFailedCurrentTest_atomic = 0; p->numAssertsCurrentTest_atomic = 0; - p->subcasesPassed.clear(); + p->fullyTraversedSubcases.clear(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); @@ -3832,9 +3881,10 @@ int Context::run() { do { // reset some of the fields for subcases (except for the set of fully passed ones) - p->should_reenter = false; - p->subcasesCurrentMaxLevel = 0; - p->subcasesStack.clear(); + p->reachedLeaf = false; + // May not be empty if previous subcase exited via exception. + p->subcaseStack.clear(); + p->currentSubcaseDepth = 0; p->shouldLogCurrentException = true; @@ -3868,9 +3918,9 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; } - if(p->should_reenter && run_test) + if(!p->nextSubcaseStack.empty() && run_test) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); - if(!p->should_reenter) + if(p->nextSubcaseStack.empty()) run_test = false; } while(run_test); diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h index 03162150..877b24c2 100644 --- a/doctest/parts/doctest_fwd.h +++ b/doctest/parts/doctest_fwd.h @@ -804,6 +804,7 @@ struct DOCTEST_INTERFACE SubcaseSignature const char* m_file; int m_line; + bool operator==(const SubcaseSignature& other) const; bool operator<(const SubcaseSignature& other) const; }; @@ -1227,6 +1228,9 @@ namespace detail { ~Subcase(); operator bool() const; + + private: + bool checkFilters(); }; template <typename L, typename R> diff --git a/examples/all_features/subcases.cpp b/examples/all_features/subcases.cpp index df42b9d0..3fad25fd 100644 --- a/examples/all_features/subcases.cpp +++ b/examples/all_features/subcases.cpp @@ -34,6 +34,26 @@ TEST_CASE("lots of nested subcases") { } } +TEST_CASE("reentering subcase via regular control flow") { + cout << endl << "root" << endl; + for (int i : { 0, 1, 2 }) { + cout << "outside of subcase" << endl; + SUBCASE("") { cout << "inside subcase " << i << endl; } + SUBCASE("") { cout << "also inside " << i << endl; } + SUBCASE("") { + if (i != 0) { FAIL(i); } + cout << "fail inside " << i << endl; + } + SUBCASE("") { + cout << "inside outside" << endl; + for (int j : { 0, 1, 2 }) { + SUBCASE("") { cout << "nested twice " << i << ", " << j << endl; } + SUBCASE("") { cout << "also twice " << i << ", " << j << endl; } + } + } + } +} + static void call_func() { SUBCASE("from function...") { MESSAGE("print me twice"); diff --git a/examples/all_features/test_output/filter_2.txt b/examples/all_features/test_output/filter_2.txt index c734cf8f..9c154add 100644 --- a/examples/all_features/test_output/filter_2.txt +++ b/examples/all_features/test_output/filter_2.txt @@ -1,6 +1,6 @@ [doctest] run with "--help" for options =============================================================================== -[doctest] test cases: 0 | 0 passed | 0 failed | 99 skipped +[doctest] test cases: 0 | 0 passed | 0 failed | 100 skipped [doctest] assertions: 0 | 0 passed | 0 failed | [doctest] Status: SUCCESS! Program code. diff --git a/examples/all_features/test_output/filter_2_xml.txt b/examples/all_features/test_output/filter_2_xml.txt index e36071cd..fa6cd147 100644 --- a/examples/all_features/test_output/filter_2_xml.txt +++ b/examples/all_features/test_output/filter_2_xml.txt @@ -104,6 +104,7 @@ <TestCase name="part of some TS" filename="test_cases_and_suites.cpp" line="0" skipped="true"/> </TestSuite> <TestSuite> + <TestCase name="reentering subcase via regular control flow" filename="subcases.cpp" line="0" skipped="true"/> <TestCase name="should fail and no output" filename="no_failures.cpp" line="0" should_fail="true" skipped="true"/> <TestCase name="should fail and no output" filename="test_cases_and_suites.cpp" line="0" should_fail="true" skipped="true"/> <TestCase name="should fail because of an exception" filename="test_cases_and_suites.cpp" line="0" skipped="true"/> @@ -141,6 +142,6 @@ <TestCase name="without a funny name:" filename="subcases.cpp" line="0" skipped="true"/> </TestSuite> <OverallResultsAsserts successes="0" failures="0"/> - <OverallResultsTestCases successes="0" failures="0" skipped="99"/> + <OverallResultsTestCases successes="0" failures="0" skipped="100"/> </doctest> Program code. diff --git a/examples/all_features/test_output/filter_3.txt b/examples/all_features/test_output/filter_3.txt index e2f2be93..7da4b5c4 100644 --- a/examples/all_features/test_output/filter_3.txt +++ b/examples/all_features/test_output/filter_3.txt @@ -1,6 +1,11 @@ [doctest] run with "--help" for options root + +root +outside of subcase +outside of subcase +outside of subcase =============================================================================== subcases.cpp(0): TEST CASE: subcases can be used in a separate function as well @@ -34,7 +39,7 @@ TEST CASE: without a funny name: subcases.cpp(0): MESSAGE: Nooo =============================================================================== -[doctest] test cases: 9 | 9 passed | 0 failed | -[doctest] assertions: 0 | 0 passed | 0 failed | +[doctest] test cases: 10 | 10 passed | 0 failed | +[doctest] assertions: 0 | 0 passed | 0 failed | [doctest] Status: SUCCESS! Program code. diff --git a/examples/all_features/test_output/filter_3_junit.txt b/examples/all_features/test_output/filter_3_junit.txt index 04f78c57..6ea54f98 100644 --- a/examples/all_features/test_output/filter_3_junit.txt +++ b/examples/all_features/test_output/filter_3_junit.txt @@ -1,9 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> root + +root +outside of subcase +outside of subcase +outside of subcase <testsuites> <testsuite name="all_features" errors="0" failures="0" tests="0"> <testcase classname="subcases.cpp" name="lots of nested subcases" status="run"/> + <testcase classname="subcases.cpp" name="reentering subcase via regular control flow" status="run"/> <testcase classname="subcases.cpp" name="subcases can be used in a separate function as well/from function.../sc1" status="run"/> <testcase classname="subcases.cpp" name=" Scenario: vectors can be sized and resized" status="run"/> <testcase classname="subcases.cpp" name="test case should fail even though the last subcase passes" status="run"/> diff --git a/examples/all_features/test_output/filter_3_xml.txt b/examples/all_features/test_output/filter_3_xml.txt index 1922de26..f662506c 100644 --- a/examples/all_features/test_output/filter_3_xml.txt +++ b/examples/all_features/test_output/filter_3_xml.txt @@ -7,6 +7,14 @@ root <OverallResultsAsserts successes="0" failures="0" test_case_success="true"/> </TestCase> + <TestCase name="reentering subcase via regular control flow" filename="subcases.cpp" line="0"> + +root +outside of subcase +outside of subcase +outside of subcase + <OverallResultsAsserts successes="0" failures="0" test_case_success="true"/> + </TestCase> <TestCase name="subcases can be used in a separate function as well" filename="subcases.cpp" line="0"> <SubCase name="from function..." filename="subcases.cpp" line="0"> <Message type="WARNING" filename="subcases.cpp" line="0"> @@ -59,6 +67,6 @@ root </TestCase> </TestSuite> <OverallResultsAsserts successes="0" failures="0"/> - <OverallResultsTestCases successes="9" failures="0"/> + <OverallResultsTestCases successes="10" failures="0"/> </doctest> Program code. diff --git a/examples/all_features/test_output/no_multi_lane_atomics.txt b/examples/all_features/test_output/no_multi_lane_atomics.txt index 490dd742..0d3888f6 100644 --- a/examples/all_features/test_output/no_multi_lane_atomics.txt +++ b/examples/all_features/test_output/no_multi_lane_atomics.txt @@ -638,6 +638,69 @@ TEST CASE: part of some TS test_cases_and_suites.cpp(0): FATAL ERROR: + +root +outside of subcase +inside subcase 0 +outside of subcase +inside subcase 1 +outside of subcase +inside subcase 2 + +root +outside of subcase +also inside 0 +outside of subcase +also inside 1 +outside of subcase +also inside 2 + +root +outside of subcase +fail inside 0 +outside of subcase +=============================================================================== +subcases.cpp(0): +TEST CASE: reentering subcase via regular control flow + +DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE): + +subcases.cpp(0): FATAL ERROR: 1 + + +root +outside of subcase +inside outside +nested twice 0, 0 +nested twice 0, 1 +nested twice 0, 2 +outside of subcase +inside outside +nested twice 1, 0 +nested twice 1, 1 +nested twice 1, 2 +outside of subcase +inside outside +nested twice 2, 0 +nested twice 2, 1 +nested twice 2, 2 + +root +outside of subcase +inside outside +also twice 0, 0 +also twice 0, 1 +also twice 0, 2 +outside of subcase +inside outside +also twice 1, 0 +also twice 1, 1 +also twice 1, 2 +outside of subcase +inside outside +also twice 2, 0 +also twice 2, 1 +also twice 2, 2 =============================================================================== test_cases_and_suites.cpp(0): TEST CASE: should fail because of an exception @@ -814,7 +877,7 @@ TEST CASE: without a funny name: subcases.cpp(0): MESSAGE: Nooo =============================================================================== -[doctest] test cases: 79 | 30 passed | 49 failed | -[doctest] assertions: 215 | 100 passed | 115 failed | +[doctest] test cases: 80 | 30 passed | 50 failed | +[doctest] assertions: 216 | 100 passed | 116 failed | [doctest] Status: FAILURE! Program code. diff --git a/examples/all_features/test_output/no_multithreading.txt b/examples/all_features/test_output/no_multithreading.txt index 490dd742..0d3888f6 100644 --- a/examples/all_features/test_output/no_multithreading.txt +++ b/examples/all_features/test_output/no_multithreading.txt @@ -638,6 +638,69 @@ TEST CASE: part of some TS test_cases_and_suites.cpp(0): FATAL ERROR: + +root +outside of subcase +inside subcase 0 +outside of subcase +inside subcase 1 +outside of subcase +inside subcase 2 + +root +outside of subcase +also inside 0 +outside of subcase +also inside 1 +outside of subcase +also inside 2 + +root +outside of subcase +fail inside 0 +outside of subcase +=============================================================================== +subcases.cpp(0): +TEST CASE: reentering subcase via regular control flow + +DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE): + +subcases.cpp(0): FATAL ERROR: 1 + + +root +outside of subcase +inside outside +nested twice 0, 0 +nested twice 0, 1 +nested twice 0, 2 +outside of subcase +inside outside +nested twice 1, 0 +nested twice 1, 1 +nested twice 1, 2 +outside of subcase +inside outside +nested twice 2, 0 +nested twice 2, 1 +nested twice 2, 2 + +root +outside of subcase +inside outside +also twice 0, 0 +also twice 0, 1 +also twice 0, 2 +outside of subcase +inside outside +also twice 1, 0 +also twice 1, 1 +also twice 1, 2 +outside of subcase +inside outside +also twice 2, 0 +also twice 2, 1 +also twice 2, 2 =============================================================================== test_cases_and_suites.cpp(0): TEST CASE: should fail because of an exception @@ -814,7 +877,7 @@ TEST CASE: without a funny name: subcases.cpp(0): MESSAGE: Nooo =============================================================================== -[doctest] test cases: 79 | 30 passed | 49 failed | -[doctest] assertions: 215 | 100 passed | 115 failed | +[doctest] test cases: 80 | 30 passed | 50 failed | +[doctest] assertions: 216 | 100 passed | 116 failed | [doctest] Status: FAILURE! Program code. diff --git a/examples/all_features/test_output/subcases.cpp.txt b/examples/all_features/test_output/subcases.cpp.txt index 0acc7bf1..8ce6f4e7 100644 --- a/examples/all_features/test_output/subcases.cpp.txt +++ b/examples/all_features/test_output/subcases.cpp.txt @@ -16,6 +16,69 @@ TEST CASE: lots of nested subcases subcases.cpp(0): FATAL ERROR: + +root +outside of subcase +inside subcase 0 +outside of subcase +inside subcase 1 +outside of subcase +inside subcase 2 + +root +outside of subcase +also inside 0 +outside of subcase +also inside 1 +outside of subcase +also inside 2 + +root +outside of subcase +fail inside 0 +outside of subcase +=============================================================================== +subcases.cpp(0): +TEST CASE: reentering subcase via regular control flow + +DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE): + +subcases.cpp(0): FATAL ERROR: 1 + + +root +outside of subcase +inside outside +nested twice 0, 0 +nested twice 0, 1 +nested twice 0, 2 +outside of subcase +inside outside +nested twice 1, 0 +nested twice 1, 1 +nested twice 1, 2 +outside of subcase +inside outside +nested twice 2, 0 +nested twice 2, 1 +nested twice 2, 2 + +root +outside of subcase +inside outside +also twice 0, 0 +also twice 0, 1 +also twice 0, 2 +outside of subcase +inside outside +also twice 1, 0 +also twice 1, 1 +also twice 1, 2 +outside of subcase +inside outside +also twice 2, 0 +also twice 2, 1 +also twice 2, 2 =============================================================================== subcases.cpp(0): TEST CASE: subcases can be used in a separate function as well @@ -225,7 +288,7 @@ TEST CASE: without a funny name: subcases.cpp(0): MESSAGE: Nooo =============================================================================== -[doctest] test cases: 9 | 5 passed | 4 failed | -[doctest] assertions: 25 | 19 passed | 6 failed | +[doctest] test cases: 10 | 5 passed | 5 failed | +[doctest] assertions: 26 | 19 passed | 7 failed | [doctest] Status: FAILURE! Program code. diff --git a/examples/all_features/test_output/subcases.cpp_junit.txt b/examples/all_features/test_output/subcases.cpp_junit.txt index d1c82380..0526b7d0 100644 --- a/examples/all_features/test_output/subcases.cpp_junit.txt +++ b/examples/all_features/test_output/subcases.cpp_junit.txt @@ -10,11 +10,71 @@ root root 2 + +root +outside of subcase +inside subcase 0 +outside of subcase +inside subcase 1 +outside of subcase +inside subcase 2 + +root +outside of subcase +also inside 0 +outside of subcase +also inside 1 +outside of subcase +also inside 2 + +root +outside of subcase +fail inside 0 +outside of subcase + +root +outside of subcase +inside outside +nested twice 0, 0 +nested twice 0, 1 +nested twice 0, 2 +outside of subcase +inside outside +nested twice 1, 0 +nested twice 1, 1 +nested twice 1, 2 +outside of subcase +inside outside +nested twice 2, 0 +nested twice 2, 1 +nested twice 2, 2 + +root +outside of subcase +inside outside +also twice 0, 0 +also twice 0, 1 +also twice 0, 2 +outside of subcase +inside outside +also twice 1, 0 +also twice 1, 1 +also twice 1, 2 +outside of subcase +inside outside +also twice 2, 0 +also twice 2, 1 +also twice 2, 2 <testsuites> - <testsuite name="all_features" errors="4" failures="5" tests="25"> + <testsuite name="all_features" errors="4" failures="5" tests="26"> <testcase classname="subcases.cpp" name="lots of nested subcases" status="run"/> <testcase classname="subcases.cpp" name="lots of nested subcases" status="run"/> <testcase classname="subcases.cpp" name="lots of nested subcases" status="run"/> + <testcase classname="subcases.cpp" name="reentering subcase via regular control flow" status="run"/> + <testcase classname="subcases.cpp" name="reentering subcase via regular control flow" status="run"/> + <testcase classname="subcases.cpp" name="reentering subcase via regular control flow" status="run"/> + <testcase classname="subcases.cpp" name="reentering subcase via regular control flow" status="run"/> + <testcase classname="subcases.cpp" name="reentering subcase via regular control flow" status="run"/> <testcase classname="subcases.cpp" name="subcases can be used in a separate function as well/from function.../sc1" status="run"/> <testcase classname="subcases.cpp" name="subcases can be used in a separate function as well/from function.../sc2" status="run"/> <testcase classname="subcases.cpp" name=" Scenario: vectors can be sized and resized/ Given: A vector with some items/ When: the size is increased/ Then: the size and capacity change" status="run"> diff --git a/examples/all_features/test_output/subcases.cpp_xml.txt b/examples/all_features/test_output/subcases.cpp_xml.txt index ad208bb4..465fcd0f 100644 --- a/examples/all_features/test_output/subcases.cpp_xml.txt +++ b/examples/all_features/test_output/subcases.cpp_xml.txt @@ -31,6 +31,133 @@ root </SubCase> <OverallResultsAsserts successes="0" failures="1" test_case_success="false"/> </TestCase> + <TestCase name="reentering subcase via regular control flow" filename="subcases.cpp" line="0"> + +root +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside subcase 0 + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside subcase 1 + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside subcase 2 + </SubCase> + +root +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +also inside 0 + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +also inside 1 + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +also inside 2 + </SubCase> + +root +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +fail inside 0 + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> + <Message type="FATAL ERROR" filename="subcases.cpp" line="0"> + <Text> + 1 + </Text> + </Message> + </SubCase> + +root +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside outside + <SubCase filename="subcases.cpp" line="0"> +nested twice 0, 0 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +nested twice 0, 1 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +nested twice 0, 2 + </SubCase> + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside outside + <SubCase filename="subcases.cpp" line="0"> +nested twice 1, 0 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +nested twice 1, 1 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +nested twice 1, 2 + </SubCase> + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside outside + <SubCase filename="subcases.cpp" line="0"> +nested twice 2, 0 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +nested twice 2, 1 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +nested twice 2, 2 + </SubCase> + </SubCase> + +root +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside outside + <SubCase filename="subcases.cpp" line="0"> +also twice 0, 0 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +also twice 0, 1 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +also twice 0, 2 + </SubCase> + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside outside + <SubCase filename="subcases.cpp" line="0"> +also twice 1, 0 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +also twice 1, 1 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +also twice 1, 2 + </SubCase> + </SubCase> +outside of subcase + <SubCase filename="subcases.cpp" line="0"> +inside outside + <SubCase filename="subcases.cpp" line="0"> +also twice 2, 0 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +also twice 2, 1 + </SubCase> + <SubCase filename="subcases.cpp" line="0"> +also twice 2, 2 + </SubCase> + </SubCase> + <OverallResultsAsserts successes="0" failures="1" test_case_success="false"/> + </TestCase> <TestCase name="subcases can be used in a separate function as well" filename="subcases.cpp" line="0"> <SubCase name="from function..." filename="subcases.cpp" line="0"> <Message type="WARNING" filename="subcases.cpp" line="0"> @@ -269,7 +396,7 @@ root <OverallResultsAsserts successes="0" failures="0" test_case_success="true"/> </TestCase> </TestSuite> - <OverallResultsAsserts successes="19" failures="6"/> - <OverallResultsTestCases successes="5" failures="4"/> + <OverallResultsAsserts successes="19" failures="7"/> + <OverallResultsTestCases successes="5" failures="5"/> </doctest> Program code. |