Skip to content

Commit dd94b9a

Browse files
committed
Fix --warn InfiniteGenerators firing even if the generator was filtered
1 parent 9f7c9f6 commit dd94b9a

5 files changed

Lines changed: 102 additions & 18 deletions

File tree

src/catch2/generators/catch_generators.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,7 @@ namespace Generators {
179179

180180
bool isFinite() const override {
181181
for (auto const& gen : m_generators) {
182-
if (!gen.isFinite()) { return false;
183-
}
182+
if (!gen.isFinite()) { return false; }
184183
}
185184
return true;
186185
}

src/catch2/internal/catch_run_context.cpp

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ namespace Catch {
196196
auto getGenerator() const -> GeneratorBasePtr const& override {
197197
return m_generator;
198198
}
199+
200+
bool isFilteredImpl() const override { return m_isFiltered; }
199201
};
200202
} // namespace
201203
}
@@ -540,17 +542,6 @@ namespace Catch {
540542
SourceLineInfo lineInfo,
541543
Generators::GeneratorBasePtr&& generator ) {
542544

543-
// TBD: Do we want to avoid the warning if the generator is filtered?
544-
if ( m_config->warnAboutInfiniteGenerators() &&
545-
!generator->isFinite() ) {
546-
// We want the semantics of `FAIL()`, but we inline it
547-
// to avoid issues with conditionally prefixed macros
548-
INTERNAL_CATCH_MSG( "FAIL",
549-
Catch::ResultWas::ExplicitFailure,
550-
Catch::ResultDisposition::Normal,
551-
"GENERATE() would run infinitely" );
552-
}
553-
554545
auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo );
555546
auto& currentTracker = m_trackerContext.currentTracker();
556547
assert(
@@ -563,11 +554,24 @@ namespace Catch {
563554
m_trackerContext,
564555
&currentTracker,
565556
CATCH_MOVE( generator ) );
566-
auto ret = newTracker.get();
557+
558+
// The warning shouldn't fire if the generator is infinite, **but** filtered down.
559+
if ( m_config->warnAboutInfiniteGenerators() &&
560+
!newTracker->m_generator->isFinite() &&
561+
!newTracker->isFiltered() ) {
562+
// We want the semantics of `FAIL()`, but we inline it
563+
// to avoid issues with conditionally prefixed macros
564+
INTERNAL_CATCH_MSG( "FAIL",
565+
Catch::ResultWas::ExplicitFailure,
566+
Catch::ResultDisposition::Normal,
567+
"GENERATE() would run infinitely" );
568+
}
569+
570+
auto returnPtr = newTracker.get();
567571
currentTracker.addChild( CATCH_MOVE( newTracker ) );
568572

569-
ret->open();
570-
return ret;
573+
returnPtr->open();
574+
return returnPtr;
571575
}
572576

573577
bool RunContext::testForMissingAssertions(Counts& assertions) {

src/catch2/internal/catch_test_case_tracker.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,28 @@ namespace TestCaseTracking {
167167
m_ctx.setCurrentTracker( this );
168168
}
169169

170+
bool SectionTracker::isFilteredImpl() const {
171+
// TBD: This is currently _very_ similar to the block in `isComplete`.
172+
// Is this neccessarily that way, or just accident of current semantics?
173+
const size_t filterIndex =
174+
m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth;
175+
176+
if ( filterIndex < m_filterRef->size() ) {
177+
// 1) New style filter must explicitly target section
178+
if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type !=
179+
PathFilter::For::Section ) {
180+
return true;
181+
}
182+
// 2) Both style filters must match the trimmed name exactly
183+
if ( m_trimmed_name !=
184+
StringRef( ( *m_filterRef )[filterIndex].filter ) ) {
185+
return true;
186+
}
187+
}
188+
189+
return false;
190+
}
191+
170192
SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
171193
: TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
172194
m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))

src/catch2/internal/catch_test_case_tracker.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <catch2/internal/catch_source_line_info.hpp>
1313
#include <catch2/internal/catch_unique_ptr.hpp>
1414
#include <catch2/internal/catch_stringref.hpp>
15+
#include <catch2/internal/catch_path_filter.hpp>
1516

1617
#include <string>
1718
#include <vector>
@@ -81,6 +82,8 @@ namespace TestCaseTracking {
8182

8283
using Children = std::vector<ITrackerPtr>;
8384

85+
virtual bool isFilteredImpl() const = 0;
86+
8487
protected:
8588
enum CycleState {
8689
NotStarted,
@@ -178,6 +181,20 @@ namespace TestCaseTracking {
178181
* for internal debug checks.
179182
*/
180183
virtual bool isGeneratorTracker() const;
184+
185+
/**
186+
* Returns true if the concrete tracker instance has a filter that applies to it.
187+
*/
188+
bool isFiltered() const {
189+
// Fast path: are there even filters for tracker in this position?
190+
const size_t filter_depth =
191+
m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth;
192+
if ( m_filterRef->size() <= filter_depth ) { return false; }
193+
194+
// Slow path: If there are filters, ask the concrete tracker.
195+
// This handles things like match-all filters for that tracker.
196+
return isFilteredImpl();
197+
}
181198
};
182199

183200
class TrackerContext {
@@ -233,6 +250,8 @@ namespace TestCaseTracking {
233250
// to not own the name, the name still has to outlive the `ITracker` parent, so
234251
// this should still be safe.
235252
StringRef m_trimmed_name;
253+
254+
bool isFilteredImpl() const override;
236255
public:
237256
SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
238257

tests/ExtraTests/CMakeLists.txt

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,16 +583,56 @@ add_executable(InfiniteGenerators ${TESTS_DIR}/X95-InfiniteGenerators.cpp)
583583
target_link_libraries(InfiniteGenerators PRIVATE Catch2::Catch2WithMain)
584584

585585
add_test(
586-
NAME Warnings::InfiniteGenerators
586+
NAME Warnings::InfiniteGenerators::NoFilterWarns
587587
COMMAND $<TARGET_FILE:InfiniteGenerators> --warn InfiniteGenerators
588588
)
589-
set_tests_properties(Warnings::InfiniteGenerators
589+
set_tests_properties(Warnings::InfiniteGenerators::NoFilterWarns
590590
PROPERTIES
591591
# One test case fails with infinite generator, but the other one runs
592592
PASS_REGULAR_EXPRESSION "test cases: 2 \\| 1 passed \\| 1 failed"
593593
TIMEOUT 5
594594
)
595595

596+
add_test(
597+
NAME Warnings::InfiniteGenerators::MatchAllFilterWarns
598+
COMMAND $<TARGET_FILE:InfiniteGenerators>
599+
--warn InfiniteGenerators
600+
--path-filter g:*
601+
)
602+
set_tests_properties(Warnings::InfiniteGenerators::MatchAllFilterWarns
603+
PROPERTIES
604+
# One test case fails with infinite generator, but the other one runs
605+
PASS_REGULAR_EXPRESSION "test cases: 2 \\| 1 passed \\| 1 failed"
606+
TIMEOUT 5
607+
)
608+
609+
add_test(
610+
NAME Warnings::InfiniteGenerators::MatchOneFilterDoesntWarn
611+
COMMAND $<TARGET_FILE:InfiniteGenerators>
612+
--warn InfiniteGenerators
613+
--path-filter g:1
614+
)
615+
set_tests_properties(Warnings::InfiniteGenerators::MatchOneFilterDoesntWarn
616+
PROPERTIES
617+
# One test case fails with infinite generator, but the other one runs
618+
PASS_REGULAR_EXPRESSION "All tests passed \\(1 assertion in 2 test cases\\)"
619+
TIMEOUT 5
620+
)
621+
622+
add_test(
623+
NAME Warnings::InfiniteGenerators::SectionFilterWarns
624+
COMMAND $<TARGET_FILE:InfiniteGenerators>
625+
--warn InfiniteGenerators
626+
--section FooBarBaz
627+
)
628+
set_tests_properties(Warnings::InfiniteGenerators::SectionFilterWarns
629+
PROPERTIES
630+
# One test case fails with infinite generator, but the other one runs
631+
PASS_REGULAR_EXPRESSION "test cases: 2 \\| 1 passed \\| 1 failed"
632+
TIMEOUT 5
633+
)
634+
635+
596636
set(EXTRA_TEST_BINARIES
597637
AllSkipped
598638
PrefixedMacros

0 commit comments

Comments
 (0)