Compare commits

...

2 Commits

Author SHA1 Message Date
Martin Hořeňovský
42a97ae44d Fix section filtering to make sense
Specifically, this commit makes the `-c`/`--section` parameter
strictly ordered and hierarchical, unlike how it behaved before,
which was a huge mess -- see #3038 for details.

Closes #3038
2025-10-14 22:14:47 +02:00
Martin Hořeňovský
fecf606a05 Add option to skip forward to the generator interface 2025-10-06 11:47:38 +02:00
28 changed files with 561 additions and 66 deletions

View File

@@ -252,9 +252,23 @@ struct IGenerator : GeneratorUntypedBase {
// Returns user-friendly string showing the current generator element
// Does not have to be overridden, IGenerator provides default implementation
virtual std::string stringifyImpl() const;
/**
* Customization point for `skipToNthElement`
*
* Does not have to be overridden, there is a default implementation.
* Can be overridden for better performance.
*
* If there are not enough elements, shall throw an error.
*
* Going backwards is not supported.
*/
virtual void skipToNthElementImpl( std::size_t n );
};
```
> `skipToNthElementImpl` was added in Catch2 vX.Y.Z
However, to be able to use your custom generator inside `GENERATE`, it
will need to be wrapped inside a `GeneratorWrapper<T>`.
`GeneratorWrapper<T>` is a value wrapper around a

View File

@@ -39,6 +39,22 @@ public:
current_number = m_dist(m_rand);
return true;
}
// Note: this improves the performance only a bit, but it is here
// to show how you can override the skip functionality.
void skipToNthElementImpl( std::size_t n ) override {
auto current_index = currentElementIndex();
assert(current_index <= n);
// We cannot jump forward the underlying generator directly,
// because we do not know how many bits each distributed number
// would consume to be generated.
for (; current_index < n; ++current_index) {
(void)m_dist(m_rand);
}
// We do not have to touch the current element index; it is handled
// by the base class.
}
};
// Avoids -Wweak-vtables

View File

@@ -7,6 +7,8 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <string>
namespace Catch {
@@ -21,6 +23,30 @@ namespace Catch {
return ret;
}
void GeneratorUntypedBase::skipToNthElementImpl( std::size_t n ) {
for ( size_t i = m_currentElementIndex; i < n; ++i ) {
bool isValid = next();
if ( !isValid ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
}
}
}
void GeneratorUntypedBase::skipToNthElement( std::size_t n ) {
if ( n < m_currentElementIndex ) {
Detail::throw_generator_exception(
"Tried to jump generator backwards" );
}
skipToNthElementImpl(n);
// Fixup tracking after moving the generator forward
// * Ensure that the correct element index is set after skipping
// * Invalidate cache
m_currentElementIndex = n;
m_stringReprCache.clear();
}
StringRef GeneratorUntypedBase::currentElementAsString() const {
if ( m_stringReprCache.empty() ) {
m_stringReprCache = stringifyImpl();

View File

@@ -35,6 +35,15 @@ namespace Catch {
//! Customization point for `currentElementAsString`
virtual std::string stringifyImpl() const = 0;
/**
* Customization point for skipping to the n-th element
*
* Defaults to successively calling `countedNext`. If there
* are not enough elements to reach the nth one, will throw
* an error.
*/
virtual void skipToNthElementImpl( std::size_t n );
public:
GeneratorUntypedBase() = default;
// Generation of copy ops is deprecated (and Clang will complain)
@@ -58,6 +67,13 @@ namespace Catch {
std::size_t currentElementIndex() const { return m_currentElementIndex; }
/**
* Moves the generator forward **to** the n-th element
*
* Cannot move backwards.
*/
void skipToNthElement( std::size_t n );
/**
* Returns generator's current element as user-friendly string.
*

View File

@@ -129,12 +129,8 @@ namespace Catch {
for ( auto const& child : m_children ) {
if ( child->isSectionTracker() &&
std::find( filters.begin(),
filters.end(),
static_cast<SectionTracker const&>(
*child )
.trimmedName() ) !=
filters.end() ) {
static_cast<SectionTracker const&>( *child )
.trimmedName() == filters[0] ) {
return true;
}
}

View File

@@ -172,9 +172,9 @@ namespace TestCaseTracking {
bool SectionTracker::isComplete() const {
bool complete = true;
if (m_filters.empty()
if ( m_filters.empty()
|| m_filters[0].empty()
|| std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
|| m_filters[0] == m_trimmed_name ) {
complete = TrackerBase::isComplete();
}
return complete;

View File

@@ -310,38 +310,23 @@ set_tests_properties(UnmatchedOutputFilter
PASS_REGULAR_EXPRESSION "No test cases matched '\\[this-tag-does-not-exist\\]'"
)
add_test(NAME FilteredSection-1 COMMAND $<TARGET_FILE:SelfTest> \#1394 -c RunSection)
set_tests_properties(FilteredSection-1 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
add_test(NAME FilteredSection-2 COMMAND $<TARGET_FILE:SelfTest> \#1394\ nested -c NestedRunSection -c s1)
set_tests_properties(FilteredSection-2 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
add_test(NAME FilteredSections::SimpleExample::1 COMMAND $<TARGET_FILE:SelfTest> \#1394 -c RunSection)
set_tests_properties(FilteredSections::SimpleExample::1 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
add_test(NAME FilteredSections::SimpleExample::2 COMMAND $<TARGET_FILE:SelfTest> \#1394\ nested -c NestedRunSection -c s1)
set_tests_properties(FilteredSections::SimpleExample::2 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
add_test(
NAME
FilteredSection::GeneratorsDontCauseInfiniteLoop-1
FilteredSections::GeneratorsDontCauseInfiniteLoop
COMMAND
$<TARGET_FILE:SelfTest> "#2025: original repro" -c "fov_0"
)
set_tests_properties(FilteredSection::GeneratorsDontCauseInfiniteLoop-1
set_tests_properties(FilteredSections::GeneratorsDontCauseInfiniteLoop
PROPERTIES
PASS_REGULAR_EXPRESSION "inside with fov: 0" # This should happen
FAIL_REGULAR_EXPRESSION "inside with fov: 1" # This would mean there was no filtering
)
# GENERATE between filtered sections (both are selected)
add_test(
NAME
FilteredSection::GeneratorsDontCauseInfiniteLoop-2
COMMAND
$<TARGET_FILE:SelfTest> "#2025: same-level sections"
-c "A"
-c "B"
--colour-mode none
)
set_tests_properties(FilteredSection::GeneratorsDontCauseInfiniteLoop-2
PROPERTIES
PASS_REGULAR_EXPRESSION "All tests passed \\(4 assertions in 1 test case\\)"
)
add_test(NAME ApprovalTests
COMMAND
Python3::Interpreter
@@ -694,6 +679,14 @@ set_tests_properties("Bazel::RngSeedEnvVar::MalformedValueIsIgnored"
PASS_REGULAR_EXPRESSION "Randomness seeded to: 17171717"
)
add_test(NAME "FilteredSections::DifferentSimpleFilters"
COMMAND
Python3::Interpreter "${CMAKE_CURRENT_LIST_DIR}/TestScripts/testSectionFiltering.py" $<TARGET_FILE:SelfTest>
)
set_tests_properties("FilteredSections::DifferentSimpleFilters"
PROPERTIES
LABELS "uses-python"
)
list(APPEND CATCH_TEST_TARGETS SelfTest)
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)

View File

@@ -168,6 +168,7 @@ Nor would this
:test-result: PASS GENERATE can combine literals and generators
:test-result: PASS Generators -- adapters
:test-result: PASS Generators -- simple
:test-result: PASS Generators can be skipped forward
:test-result: PASS Generators internals
:test-result: PASS Greater-than inequalities with different epsilons
:test-result: PASS Hashers with different seed produce different hash with same test case

View File

@@ -166,6 +166,7 @@
:test-result: PASS GENERATE can combine literals and generators
:test-result: PASS Generators -- adapters
:test-result: PASS Generators -- simple
:test-result: PASS Generators can be skipped forward
:test-result: PASS Generators internals
:test-result: PASS Greater-than inequalities with different epsilons
:test-result: PASS Hashers with different seed produce different hash with same test case

View File

@@ -787,6 +787,13 @@ Generators.tests.cpp:<line number>: passed: j < i for: -1 < 3
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 1
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 2
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 3
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 0 for: 0 == 0
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 3 )
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 6 )
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 123 for: 123 == 123
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
@@ -2888,7 +2895,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
test cases: 436 | 318 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2310 | 2112 passed | 157 failed | 41 failed as expected

View File

@@ -785,6 +785,13 @@ Generators.tests.cpp:<line number>: passed: j < i for: -1 < 3
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 1
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 2
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 3
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 0 for: 0 == 0
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 3 )
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 6 )
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 123 for: 123 == 123
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
@@ -2877,7 +2884,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
test cases: 436 | 318 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2310 | 2112 passed | 157 failed | 41 failed as expected

View File

@@ -1719,6 +1719,6 @@ due to unexpected exception with message:
Why would you throw a std::string?
===============================================================================
test cases: 435 | 335 passed | 76 failed | 7 skipped | 17 failed as expected
assertions: 2282 | 2105 passed | 136 failed | 41 failed as expected
test cases: 436 | 336 passed | 76 failed | 7 skipped | 17 failed as expected
assertions: 2289 | 2112 passed | 136 failed | 41 failed as expected

View File

@@ -5831,6 +5831,43 @@ Generators.tests.cpp:<line number>: PASSED:
with expansion:
12 > 3
-------------------------------------------------------------------------------
Generators can be skipped forward
-------------------------------------------------------------------------------
GeneratorsImpl.tests.cpp:<line number>
...............................................................................
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.currentElementIndex() == 0 )
with expansion:
0 == 0
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.currentElementIndex() == 3 )
with expansion:
3 == 3
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.get() == 3 )
with expansion:
3 == 3
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.currentElementIndex() == 5 )
with expansion:
5 == 5
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.get() == 5 )
with expansion:
5 == 5
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS( generator.skipToNthElement( 3 ) )
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS( generator.skipToNthElement( 6 ) )
-------------------------------------------------------------------------------
Generators internals
Single value
@@ -19295,6 +19332,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
test cases: 436 | 318 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2310 | 2112 passed | 157 failed | 41 failed as expected

View File

@@ -5829,6 +5829,43 @@ Generators.tests.cpp:<line number>: PASSED:
with expansion:
12 > 3
-------------------------------------------------------------------------------
Generators can be skipped forward
-------------------------------------------------------------------------------
GeneratorsImpl.tests.cpp:<line number>
...............................................................................
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.currentElementIndex() == 0 )
with expansion:
0 == 0
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.currentElementIndex() == 3 )
with expansion:
3 == 3
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.get() == 3 )
with expansion:
3 == 3
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.currentElementIndex() == 5 )
with expansion:
5 == 5
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( generator.get() == 5 )
with expansion:
5 == 5
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS( generator.skipToNthElement( 3 ) )
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS( generator.skipToNthElement( 6 ) )
-------------------------------------------------------------------------------
Generators internals
Single value
@@ -19284,6 +19321,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
test cases: 436 | 318 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2310 | 2112 passed | 157 failed | 41 failed as expected

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2315" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2322" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="random-seed" value="1"/>
<property name="filters" value="&quot;*&quot; ~[!nonportable] ~[!benchmark] ~[approvals]"/>
@@ -837,6 +837,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Generators -- simple" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators -- simple/one" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators -- simple/two" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators can be skipped forward" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Single value" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Preset values" time="{duration}" status="run"/>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2315" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2322" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="random-seed" value="1"/>
<property name="filters" value="&quot;*&quot; ~[!nonportable] ~[!benchmark] ~[approvals]"/>
@@ -836,6 +836,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Generators -- simple" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators -- simple/one" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators -- simple/two" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators can be skipped forward" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Single value" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Preset values" time="{duration}" status="run"/>

View File

@@ -144,6 +144,7 @@ at AssertionHandler.tests.cpp:<line number>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp">
<testCase name="Filter generator throws exception for empty generator" duration="{duration}"/>
<testCase name="Generators can be skipped forward" duration="{duration}"/>
<testCase name="Generators internals" duration="{duration}"/>
<testCase name="Generators internals/Single value" duration="{duration}"/>
<testCase name="Generators internals/Preset values" duration="{duration}"/>

View File

@@ -143,6 +143,7 @@ at AssertionHandler.tests.cpp:<line number>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp">
<testCase name="Filter generator throws exception for empty generator" duration="{duration}"/>
<testCase name="Generators can be skipped forward" duration="{duration}"/>
<testCase name="Generators internals" duration="{duration}"/>
<testCase name="Generators internals/Single value" duration="{duration}"/>
<testCase name="Generators internals/Preset values" duration="{duration}"/>

View File

@@ -1430,6 +1430,20 @@ ok {test-number} - 4u * i > str.size() for: 12 > 1
ok {test-number} - 4u * i > str.size() for: 12 > 2
# Generators -- simple
ok {test-number} - 4u * i > str.size() for: 12 > 3
# Generators can be skipped forward
ok {test-number} - generator.currentElementIndex() == 0 for: 0 == 0
# Generators can be skipped forward
ok {test-number} - generator.currentElementIndex() == 3 for: 3 == 3
# Generators can be skipped forward
ok {test-number} - generator.get() == 3 for: 3 == 3
# Generators can be skipped forward
ok {test-number} - generator.currentElementIndex() == 5 for: 5 == 5
# Generators can be skipped forward
ok {test-number} - generator.get() == 5 for: 5 == 5
# Generators can be skipped forward
ok {test-number} - generator.skipToNthElement( 3 )
# Generators can be skipped forward
ok {test-number} - generator.skipToNthElement( 6 )
# Generators internals
ok {test-number} - gen.get() == 123 for: 123 == 123
# Generators internals
@@ -4627,5 +4641,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} -
# xmlentitycheck
ok {test-number} -
1..2315
1..2322

View File

@@ -1428,6 +1428,20 @@ ok {test-number} - 4u * i > str.size() for: 12 > 1
ok {test-number} - 4u * i > str.size() for: 12 > 2
# Generators -- simple
ok {test-number} - 4u * i > str.size() for: 12 > 3
# Generators can be skipped forward
ok {test-number} - generator.currentElementIndex() == 0 for: 0 == 0
# Generators can be skipped forward
ok {test-number} - generator.currentElementIndex() == 3 for: 3 == 3
# Generators can be skipped forward
ok {test-number} - generator.get() == 3 for: 3 == 3
# Generators can be skipped forward
ok {test-number} - generator.currentElementIndex() == 5 for: 5 == 5
# Generators can be skipped forward
ok {test-number} - generator.get() == 5 for: 5 == 5
# Generators can be skipped forward
ok {test-number} - generator.skipToNthElement( 3 )
# Generators can be skipped forward
ok {test-number} - generator.skipToNthElement( 6 )
# Generators internals
ok {test-number} - gen.get() == 123 for: 123 == 123
# Generators internals
@@ -4616,5 +4630,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} -
# xmlentitycheck
ok {test-number} -
1..2315
1..2322

View File

@@ -414,6 +414,8 @@
##teamcity[testFinished name='Generators -- adapters' duration="{duration}"]
##teamcity[testStarted name='Generators -- simple']
##teamcity[testFinished name='Generators -- simple' duration="{duration}"]
##teamcity[testStarted name='Generators can be skipped forward']
##teamcity[testFinished name='Generators can be skipped forward' duration="{duration}"]
##teamcity[testStarted name='Generators internals']
##teamcity[testFinished name='Generators internals' duration="{duration}"]
##teamcity[testStarted name='Greater-than inequalities with different epsilons']

View File

@@ -414,6 +414,8 @@
##teamcity[testFinished name='Generators -- adapters' duration="{duration}"]
##teamcity[testStarted name='Generators -- simple']
##teamcity[testFinished name='Generators -- simple' duration="{duration}"]
##teamcity[testStarted name='Generators can be skipped forward']
##teamcity[testFinished name='Generators can be skipped forward' duration="{duration}"]
##teamcity[testStarted name='Generators internals']
##teamcity[testFinished name='Generators internals' duration="{duration}"]
##teamcity[testStarted name='Greater-than inequalities with different epsilons']

View File

@@ -6605,6 +6605,65 @@ Approx( 1.30000000000000004 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Generators can be skipped forward" tags="[generators]" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.currentElementIndex() == 0
</Original>
<Expanded>
0 == 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.currentElementIndex() == 3
</Original>
<Expanded>
3 == 3
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.get() == 3
</Original>
<Expanded>
3 == 3
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.currentElementIndex() == 5
</Original>
<Expanded>
5 == 5
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.get() == 5
</Original>
<Expanded>
5 == 5
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.skipToNthElement( 3 )
</Original>
<Expanded>
generator.skipToNthElement( 3 )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.skipToNthElement( 6 )
</Original>
<Expanded>
generator.skipToNthElement( 6 )
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Generators internals" tags="[generators][internals]" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Section name="Single value" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
@@ -22324,6 +22383,6 @@ Approx( -1.95996398454005449 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<OverallResults successes="2105" failures="157" expectedFailures="41" skips="12"/>
<OverallResultsCases successes="317" failures="95" expectedFailures="17" skips="6"/>
<OverallResults successes="2112" failures="157" expectedFailures="41" skips="12"/>
<OverallResultsCases successes="318" failures="95" expectedFailures="17" skips="6"/>
</Catch2TestRun>

View File

@@ -6605,6 +6605,65 @@ Approx( 1.30000000000000004 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Generators can be skipped forward" tags="[generators]" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.currentElementIndex() == 0
</Original>
<Expanded>
0 == 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.currentElementIndex() == 3
</Original>
<Expanded>
3 == 3
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.get() == 3
</Original>
<Expanded>
3 == 3
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.currentElementIndex() == 5
</Original>
<Expanded>
5 == 5
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.get() == 5
</Original>
<Expanded>
5 == 5
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.skipToNthElement( 3 )
</Original>
<Expanded>
generator.skipToNthElement( 3 )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
generator.skipToNthElement( 6 )
</Original>
<Expanded>
generator.skipToNthElement( 6 )
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Generators internals" tags="[generators][internals]" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Section name="Single value" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
@@ -22323,6 +22382,6 @@ Approx( -1.95996398454005449 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<OverallResults successes="2105" failures="157" expectedFailures="41" skips="12"/>
<OverallResultsCases successes="317" failures="95" expectedFailures="17" skips="6"/>
<OverallResults successes="2112" failures="157" expectedFailures="41" skips="12"/>
<OverallResultsCases successes="318" failures="95" expectedFailures="17" skips="6"/>
</Catch2TestRun>

View File

@@ -7,6 +7,7 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/internal/catch_optional.hpp>
@@ -170,3 +171,64 @@ TEST_CASE( "Decomposer checks that the argument is 0 when handling "
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
}
TEST_CASE( "foo", "[approvals]" ) {
SECTION( "A" ) {
SECTION( "B1" ) { REQUIRE( true ); }
SECTION( "B2" ) { REQUIRE( true ); }
SECTION( "B3" ) { REQUIRE( true ); }
}
}
TEST_CASE( "bar", "[approvals]" ) {
REQUIRE( true );
SECTION( "A" ) {
SECTION( "B1" ) { REQUIRE( true ); }
SECTION( "B2" ) { REQUIRE( true ); }
SECTION( "B3" ) { REQUIRE( true ); }
}
REQUIRE( true );
}
TEST_CASE( "baz", "[approvals]" ) {
SECTION( "A" ) { REQUIRE( true ); }
auto _ = GENERATE( 1, 2, 3 );
(void)_;
SECTION( "B" ) { REQUIRE( true ); }
}
TEST_CASE( "qux", "[approvals]" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
auto _ = GENERATE( 1, 2, 3 );
(void)_;
SECTION( "B" ) { REQUIRE( true ); }
REQUIRE( true );
}
TEST_CASE( "corge", "[approvals]" ) {
REQUIRE( true );
SECTION( "A" ) {
REQUIRE( true );
}
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
REQUIRE( true );
}
TEST_CASE("grault", "[approvals]") {
REQUIRE( true );
SECTION( "A" ) {
REQUIRE( true );
}
SECTION("B") {
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
REQUIRE( true );
}

View File

@@ -85,7 +85,7 @@ TEST_CASE("Generators internals", "[generators][internals]") {
filter([](int) { return false; }, values({ 1, 2, 3 })),
Catch::GeneratorException);
}
// Non-trivial usage
SECTION("Out-of-line predicates are copied into the generator") {
auto evilNumber = Catch::Detail::make_unique<int>(2);
@@ -586,3 +586,21 @@ TEST_CASE("from_range(container) supports ADL begin/end and arrays", "[generator
}
}
TEST_CASE( "Generators can be skipped forward", "[generators]" ) {
auto generator = Catch::Generators::FixedValuesGenerator<int>( { 0, 1, 2, 3, 4, 5 } );
REQUIRE( generator.currentElementIndex() == 0 );
generator.skipToNthElement( 3 );
REQUIRE( generator.currentElementIndex() == 3 );
REQUIRE( generator.get() == 3 );
generator.skipToNthElement( 5 );
REQUIRE( generator.currentElementIndex() == 5 );
REQUIRE( generator.get() == 5 );
// Backwards
REQUIRE_THROWS( generator.skipToNthElement( 3 ) );
// Past the end
REQUIRE_THROWS( generator.skipToNthElement( 6 ) );
}

View File

@@ -354,27 +354,9 @@ TEST_CASE("#1514: stderr/stdout is not captured in tests aborted by an exception
FAIL("1514");
}
TEST_CASE( "#2025: -c shouldn't cause infinite loop", "[sections][generators][regression][.approvals]" ) {
SECTION( "Check cursor from buffer offset" ) {
auto bufPos = GENERATE_REF( range( 0, 44 ) );
WHEN( "Buffer position is " << bufPos ) { REQUIRE( 1 == 1 ); }
}
}
TEST_CASE("#2025: original repro", "[sections][generators][regression][.approvals]") {
auto fov = GENERATE(true, false);
DYNAMIC_SECTION("fov_" << fov) {
std::cout << "inside with fov: " << fov << '\n';
}
}
TEST_CASE("#2025: same-level sections", "[sections][generators][regression][.approvals]") {
SECTION("A") {
SUCCEED();
}
auto i = GENERATE(1, 2, 3);
SECTION("B") {
REQUIRE(i < 4);
}
}

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python3
# Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
"""
This test script verifies the behaviour of the legacy section filtering
using `-c`, `--section` CLI parameters.
This is done by having a hardcoded set of test filter + section filter
combinations, together with the expected number of assertions that will
be run inside the test for given filter combo.
"""
import os
import subprocess
import sys
from typing import Tuple, List
import xml.etree.ElementTree as ET
def make_cli_filter(section_names: Tuple[str, ...]) -> List[str]:
final = []
for name in section_names:
final.append('--section')
final.append(name)
return final
def run_one_test(binary_path: str, test_name: str, section_names: Tuple[str, ...], expected_assertions: int):
cmd = [
binary_path,
'--reporter', 'xml',
test_name
]
cmd.extend(make_cli_filter(section_names))
try:
ret = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
universal_newlines=True,
)
stdout = ret.stdout
except subprocess.SubprocessError as ex:
print('Could not run "{}"'.format(cmd))
print("Return code: {}".format(ex.returncode))
print("stdout: {}".format(ex.stdout))
print("stderr: {}".format(ex.stderr))
raise
try:
tree = ET.fromstring(stdout)
except ET.ParseError as ex:
print("Invalid XML: '{}'".format(ex))
raise
# Validate that we ran exactly 1 test case, and it passed
test_case_stats = tree.find('OverallResultsCases')
expected_testcases = {'successes' : '1', 'failures' : '0', 'expectedFailures': '0', 'skips': '0'}
assert test_case_stats.attrib == expected_testcases, f'We did not run single passing test case as expected. {test_name}: {test_case_stats.attrib}'
# Validate that we got exactly the expected number of passing assertions
expected_assertions = {'successes' : str(expected_assertions), 'failures' : '0', 'expectedFailures': '0', 'skips': '0'}
assertion_stats = tree.find('OverallResults')
assert assertion_stats.attrib == expected_assertions, f'"{test_name}": {assertion_stats.attrib} vs {expected_assertions}'
# Inputs taken from issue #3038
tests = {
'foo': (
((), 3),
(('A',), 3),
(('A', 'B'), 0),
(('A', 'B1'), 1),
(('A', 'B2'), 1),
(('A', 'B1', 'B2'), 1),
(('A', 'B2', 'XXXX'), 1),
),
'bar': (
((), 9),
(('A',), 9),
(('A', 'B1'), 3),
(('XXXX',), 2),
(('B1',), 2),
(('A', 'B1', 'B2'), 3),
),
'baz': (
((), 4),
(('A',), 1),
(('A', 'B'), 1),
(('A', 'XXXX'), 1),
(('B',), 3),
(('XXXX',), 0),
),
'qux': (
((), 12),
(('A',), 7),
(('B',), 9),
(('B', 'XXXX'), 9),
(('XXXX',), 6),
),
'corge': (
((), 12),
(('i=2',), 7),
(('i=3',), 7),
),
'grault': (
((), 12),
(('A',), 3),
(('B',), 9),
(('B', 'i=1'), 7),
(('B', 'XXXX'), 6),
),
}
if len(sys.argv) != 2:
print("Wrong number of arguments, expected just the path to Catch2 SelfTest binary")
exit(1)
bin_path = os.path.abspath(sys.argv[1])
for test_filter, specs in tests.items():
for section_path, expected_assertions in specs:
run_one_test(bin_path, test_filter, section_path, expected_assertions)