Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMartin Hořeňovský2026-09-06 19:02:48 +0200
committerMartin Hořeňovský2026-09-06 20:08:25 +0200
commit5fe473f202a322ab10ee10bf64fcae63593286cf (patch)
tree873305a2b47405d89c52ea0de87eb1d0d404c157
parent897d8043eb192d0fc361c6339ce13e1cabe481c4 (diff)
downloadcatch3-5fe473f202a322ab10ee10bf64fcae63593286cf.tar.gz
Faster check for duplicated test cases
By avoiding `std::set`, we save 1 allocation per checked test case, and the sort + pairwise check has much nicer cache behaviour, leading to much faster check. Overall, the check is only a tiny part of the startup overhead, but the improvement is large enough that it ends up measurable in binaries with many tests (e.g. `SelfTest`).
-rw-r--r--src/catch2/catch_test_case_info.cpp6
-rw-r--r--src/catch2/catch_test_case_info.hpp4
-rw-r--r--src/catch2/internal/catch_test_case_registry_impl.cpp49
3 files changed, 41 insertions, 18 deletions
diff --git a/src/catch2/catch_test_case_info.cpp b/src/catch2/catch_test_case_info.cpp
index 9d64e532..d775b89a 100644
--- a/src/catch2/catch_test_case_info.cpp
+++ b/src/catch2/catch_test_case_info.cpp
@@ -259,4 +259,10 @@ namespace Catch {
return lhs.tags < rhs.tags;
}
+ bool operator==( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) {
+ return lhs.name == rhs.name
+ && lhs.className == rhs.className
+ && lhs.tags == rhs.tags;
+ }
+
} // end namespace Catch
diff --git a/src/catch2/catch_test_case_info.hpp b/src/catch2/catch_test_case_info.hpp
index 3466660c..fc50e725 100644
--- a/src/catch2/catch_test_case_info.hpp
+++ b/src/catch2/catch_test_case_info.hpp
@@ -82,7 +82,9 @@ namespace Catch {
//! Orders by name, classname and tags
friend bool operator<( TestCaseInfo const& lhs,
TestCaseInfo const& rhs );
-
+ //! Compares name, classname and tags
+ friend bool operator==( TestCaseInfo const& lhs,
+ TestCaseInfo const& rhs );
std::string tagsAsString() const;
diff --git a/src/catch2/internal/catch_test_case_registry_impl.cpp b/src/catch2/internal/catch_test_case_registry_impl.cpp
index e0d7c139..3c1bdd4b 100644
--- a/src/catch2/internal/catch_test_case_registry_impl.cpp
+++ b/src/catch2/internal/catch_test_case_registry_impl.cpp
@@ -17,7 +17,6 @@
#include <catch2/internal/catch_test_case_info_hasher.hpp>
#include <algorithm>
-#include <set>
namespace Catch {
@@ -27,23 +26,39 @@ namespace Catch {
static void enforceNoDuplicateTestCases(
std::vector<TestCaseHandle> const& tests ) {
- auto testInfoCmp = []( TestCaseInfo const* lhs,
- TestCaseInfo const* rhs ) {
- return *lhs < *rhs;
- };
- std::set<TestCaseInfo const*, decltype( testInfoCmp )&> seenTests(
- testInfoCmp );
+ // Flat set instead of std::set for performance.
+ std::vector<TestCaseInfo const*> seenTests;
+ seenTests.reserve( tests.size() );
for ( auto const& test : tests ) {
- const auto infoPtr = &test.getTestCaseInfo();
- const auto prev = seenTests.insert( infoPtr );
- CATCH_ENFORCE( prev.second,
- "error: test case \""
- << infoPtr->name << "\", with tags \""
- << infoPtr->tagsAsString()
- << "\" already defined.\n"
- << "\tFirst seen at "
- << ( *prev.first )->lineInfo << "\n"
- << "\tRedefined at " << infoPtr->lineInfo );
+ seenTests.push_back( &test.getTestCaseInfo() );
+ }
+ // We want to keep order of ~equal~ tests
+ // stable_sort so that duplicates are reported with the
+ // registration order preserved ("first seen" really is first)
+ std::stable_sort(
+ seenTests.begin(),
+ seenTests.end(),
+ []( TestCaseInfo const* lhs, TestCaseInfo const* rhs ) {
+ return *lhs < *rhs;
+ }
+ );
+ const auto duplicate = std::adjacent_find(
+ seenTests.begin(),
+ seenTests.end(),
+ []( TestCaseInfo const* lhs, TestCaseInfo const* rhs ) {
+ return *lhs == *rhs;
+ }
+ );
+
+ if ( duplicate != seenTests.end() ) {
+ const auto infoPtr = *std::next( duplicate );
+ CATCH_ERROR( "error: test case \""
+ << infoPtr->name << "\", with tags \""
+ << infoPtr->tagsAsString()
+ << "\" already defined.\n"
+ << "\tFirst seen at "
+ << ( *duplicate )->lineInfo << "\n"
+ << "\tRedefined at " << infoPtr->lineInfo );
}
}