Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/onqtam/doctest.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan <29021710+Saalvage@users.noreply.github.com>2022-04-25 12:01:11 +0300
committerGitHub <noreply@github.com>2022-04-25 12:01:11 +0300
commiteb8b04a2b1db23ea5665cd8b5cbe859beb0ef80d (patch)
treec21c5461eeacba80a477bfaa0b0ae3ef494daacf
parent0b79b49aac67f393aad4b90d578b1a562c7a19e4 (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.h180
-rw-r--r--doctest/parts/doctest.cpp176
-rw-r--r--doctest/parts/doctest_fwd.h4
-rw-r--r--examples/all_features/subcases.cpp20
-rw-r--r--examples/all_features/test_output/filter_2.txt2
-rw-r--r--examples/all_features/test_output/filter_2_xml.txt3
-rw-r--r--examples/all_features/test_output/filter_3.txt9
-rw-r--r--examples/all_features/test_output/filter_3_junit.txt6
-rw-r--r--examples/all_features/test_output/filter_3_xml.txt10
-rw-r--r--examples/all_features/test_output/no_multi_lane_atomics.txt67
-rw-r--r--examples/all_features/test_output/no_multithreading.txt67
-rw-r--r--examples/all_features/test_output/subcases.cpp.txt67
-rw-r--r--examples/all_features/test_output/subcases.cpp_junit.txt62
-rw-r--r--examples/all_features/test_output/subcases.cpp_xml.txt131
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.