2
0
mirror of https://github.com/catchorg/Catch2 synced 2026-02-23 16:22:10 +00:00

Add --warn InfiniteGenerators

This commit is contained in:
Martin Hořeňovský
2026-02-10 09:16:03 +01:00
parent d079ee13ab
commit daadf42a0e
18 changed files with 93 additions and 23 deletions

View File

@@ -358,10 +358,13 @@ There are currently two warnings implemented:
// (e.g. `REQUIRE`) is encountered.
UnmatchedTestSpec // Fail test run if any of the CLI test specs did
// not match any tests.
InfiniteGenerators // Fail if GENERATE would run infinitely
```
> `UnmatchedTestSpec` was introduced in Catch2 3.0.1.
> `InfiniteGenerators` was introduced in Catch2 vX.Y.Z
<a id="reporting-timings"></a>
## Reporting timings

View File

@@ -197,6 +197,9 @@ namespace Catch {
bool Config::warnAboutUnmatchedTestSpecs() const {
return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
}
bool Config::warnAboutInfiniteGenerators() const {
return !!( m_data.warnings & WarnAbout::InfiniteGenerator );
}
bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; }
ShowDurations Config::showDurations() const { return m_data.showDurations; }
double Config::minDuration() const { return m_data.minDuration; }

View File

@@ -124,6 +124,7 @@ namespace Catch {
bool includeSuccessfulResults() const override;
bool warnAboutMissingAssertions() const override;
bool warnAboutUnmatchedTestSpecs() const override;
bool warnAboutInfiniteGenerators() const override;
bool zeroTestsCountAsSuccess() const override;
ShowDurations showDurations() const override;
double minDuration() const override;

View File

@@ -29,6 +29,8 @@ namespace Catch {
NoAssertions = 0x01,
//! A command line test spec matched no test cases
UnmatchedTestSpec = 0x02,
//! The resulting generator in GENERATE is infinite
InfiniteGenerator = 0x04,
}; };
enum class ShowDurations {
@@ -71,6 +73,7 @@ namespace Catch {
virtual bool shouldDebugBreak() const = 0;
virtual bool warnAboutMissingAssertions() const = 0;
virtual bool warnAboutUnmatchedTestSpecs() const = 0;
virtual bool warnAboutInfiniteGenerators() const = 0;
virtual bool zeroTestsCountAsSuccess() const = 0;
virtual int abortAfter() const = 0;
virtual bool showInvisibles() const = 0;

View File

@@ -32,6 +32,9 @@ namespace Catch {
} else if ( warning == "UnmatchedTestSpec" ) {
config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::UnmatchedTestSpec);
return ParserResult::ok( ParseResultType::Matched );
} else if ( warning == "InfiniteGenerators" ) {
config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::InfiniteGenerator);
return ParserResult::ok( ParseResultType::Matched );
}
return ParserResult ::runtimeError(

View File

@@ -21,7 +21,9 @@
#include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_thread_local.hpp>
#include <catch2/internal/catch_unreachable.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/catch_test_macros.hpp>
#include <cassert>
#include <algorithm>
@@ -495,6 +497,11 @@ namespace Catch {
currentTracker.addChild( CATCH_MOVE( newTracker ) );
ret->setGenerator( CATCH_MOVE( generator ) );
if ( m_config->warnAboutInfiniteGenerators() &&
!ret->getGenerator()->isFinite() ) {
// TBD: Would it be better to expand this macro inline?
FAIL( "GENERATE() would run infinitely" );
}
ret->open();
return ret;
}

View File

@@ -169,7 +169,7 @@ if(CATCH_ENABLE_COVERAGE)
endif()
# configure unit tests via CTest
add_test(NAME RunTests COMMAND $<TARGET_FILE:SelfTest> --order rand --rng-seed time)
add_test(NAME RunTests COMMAND $<TARGET_FILE:SelfTest> --order rand --rng-seed time --warn InfiniteGenerators)
set_tests_properties(RunTests PROPERTIES
FAIL_REGULAR_EXPRESSION "Filters:"
COST 15

View File

@@ -589,3 +589,16 @@ set_tests_properties(ThreadSafetyTests::UnscopedMessagesAndAssertions
PASS_REGULAR_EXPRESSION "assertions: 401 \\| 401 failed as expected"
RUN_SERIAL ON
)
add_executable(InfiniteGenerators ${TESTS_DIR}/X95-InfiniteGenerators.cpp)
target_link_libraries(InfiniteGenerators PRIVATE Catch2::Catch2WithMain)
add_test(
NAME Warnings::InfiniteGenerators
COMMAND $<TARGET_FILE:InfiniteGenerators> --warn InfiniteGenerators
)
set_tests_properties(Warnings::InfiniteGenerators
PROPERTIES
PASS_REGULAR_EXPRESSION "test cases: 1 \\| 1 failed"
TIMEOUT 5
)

View File

@@ -0,0 +1,36 @@
// 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
/**\file
* Checks that GENERATE over infinite generator errors out when the tests are
* run with `-warn InfiniteGenerators`
*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
namespace {
static int ONE = 1;
class infinite_generator : public Catch::Generators::IGenerator<int> {
public:
int const& get() const override { return ONE; }
bool next() override { return true; }
auto isFinite() const -> bool override { return false; }
};
static auto make_infinite_generator()
-> Catch::Generators::GeneratorWrapper<int> {
return { new infinite_generator() };
}
} // namespace
TEST_CASE() {
auto _ = GENERATE( make_infinite_generator() );
}

View File

@@ -1446,8 +1446,8 @@ TestSpecParser.tests.cpp:<line number>: passed: spec.matches( testCase ) for: tr
CmdLine.tests.cpp:<line number>: passed: cli.parse( { "test", "-w", "NoAssertions" } ) for: {?}
CmdLine.tests.cpp:<line number>: passed: config.warnings == WarnAbout::NoAssertions for: 1 == 1
CmdLine.tests.cpp:<line number>: passed: !(cli.parse( { "test", "-w", "NoTests" } )) for: !{?}
CmdLine.tests.cpp:<line number>: passed: cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } ) for: {?}
CmdLine.tests.cpp:<line number>: passed: config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) for: 3 == 3
CmdLine.tests.cpp:<line number>: passed: cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } ) for: {?}
CmdLine.tests.cpp:<line number>: passed: config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) for: 7 == 7
Condition.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Condition.tests.cpp:<line number>: passed: p == pNULL for: 0 == 0
Condition.tests.cpp:<line number>: passed: p != 0 for: 0x<hex digits> != 0

View File

@@ -1444,8 +1444,8 @@ TestSpecParser.tests.cpp:<line number>: passed: spec.matches( testCase ) for: tr
CmdLine.tests.cpp:<line number>: passed: cli.parse( { "test", "-w", "NoAssertions" } ) for: {?}
CmdLine.tests.cpp:<line number>: passed: config.warnings == WarnAbout::NoAssertions for: 1 == 1
CmdLine.tests.cpp:<line number>: passed: !(cli.parse( { "test", "-w", "NoTests" } )) for: !{?}
CmdLine.tests.cpp:<line number>: passed: cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } ) for: {?}
CmdLine.tests.cpp:<line number>: passed: config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) for: 3 == 3
CmdLine.tests.cpp:<line number>: passed: cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } ) for: {?}
CmdLine.tests.cpp:<line number>: passed: config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) for: 7 == 7
Condition.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Condition.tests.cpp:<line number>: passed: p == pNULL for: 0 == 0
Condition.tests.cpp:<line number>: passed: p != 0 for: 0x<hex digits> != 0

View File

@@ -9425,14 +9425,14 @@ CmdLine.tests.cpp:<line number>
...............................................................................
CmdLine.tests.cpp:<line number>: PASSED:
REQUIRE( cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } ) )
REQUIRE( cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } ) )
with expansion:
{?}
CmdLine.tests.cpp:<line number>: PASSED:
REQUIRE( config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) )
REQUIRE( config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) )
with expansion:
3 == 3
7 == 7
-------------------------------------------------------------------------------
Pointers can be compared to null

View File

@@ -9423,14 +9423,14 @@ CmdLine.tests.cpp:<line number>
...............................................................................
CmdLine.tests.cpp:<line number>: PASSED:
REQUIRE( cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } ) )
REQUIRE( cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } ) )
with expansion:
{?}
CmdLine.tests.cpp:<line number>: PASSED:
REQUIRE( config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) )
REQUIRE( config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) )
with expansion:
3 == 3
7 == 7
-------------------------------------------------------------------------------
Pointers can be compared to null

View File

@@ -2371,9 +2371,9 @@ ok {test-number} - config.warnings == WarnAbout::NoAssertions for: 1 == 1
# Parsing warnings
ok {test-number} - !(cli.parse( { "test", "-w", "NoTests" } )) for: !{?}
# Parsing warnings
ok {test-number} - cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } ) for: {?}
ok {test-number} - cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } ) for: {?}
# Parsing warnings
ok {test-number} - config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) for: 3 == 3
ok {test-number} - config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) for: 7 == 7
# Pointers can be compared to null
ok {test-number} - p == 0 for: 0 == 0
# Pointers can be compared to null

View File

@@ -2369,9 +2369,9 @@ ok {test-number} - config.warnings == WarnAbout::NoAssertions for: 1 == 1
# Parsing warnings
ok {test-number} - !(cli.parse( { "test", "-w", "NoTests" } )) for: !{?}
# Parsing warnings
ok {test-number} - cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } ) for: {?}
ok {test-number} - cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } ) for: {?}
# Parsing warnings
ok {test-number} - config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) for: 3 == 3
ok {test-number} - config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) for: 7 == 7
# Pointers can be compared to null
ok {test-number} - p == 0 for: 0 == 0
# Pointers can be compared to null

View File

@@ -11291,7 +11291,7 @@ Approx( 1.21999999999999997 )
<Section name="Combining multiple warnings" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } )
cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } )
</Original>
<Expanded>
{?}
@@ -11299,10 +11299,10 @@ Approx( 1.21999999999999997 )
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec )
config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator )
</Original>
<Expanded>
3 == 3
7 == 7
</Expanded>
</Expression>
<OverallResults successes="2" failures="0" expectedFailures="0" skipped="false"/>

View File

@@ -11291,7 +11291,7 @@ Approx( 1.21999999999999997 )
<Section name="Combining multiple warnings" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec" } )
cli.parse( { "test", "--warn", "NoAssertions", "--warn", "UnmatchedTestSpec", "--warn", "InfiniteGenerators" } )
</Original>
<Expanded>
{?}
@@ -11299,10 +11299,10 @@ Approx( 1.21999999999999997 )
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec )
config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator )
</Original>
<Expanded>
3 == 3
7 == 7
</Expanded>
</Expression>
<OverallResults successes="2" failures="0" expectedFailures="0" skipped="false"/>

View File

@@ -413,9 +413,10 @@ TEST_CASE( "Parsing warnings", "[cli][warnings]" ) {
SECTION( "Combining multiple warnings" ) {
REQUIRE( cli.parse( { "test",
"--warn", "NoAssertions",
"--warn", "UnmatchedTestSpec" } ) );
"--warn", "UnmatchedTestSpec",
"--warn", "InfiniteGenerators" } ) );
REQUIRE( config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec ) );
REQUIRE( config.warnings == ( WarnAbout::NoAssertions | WarnAbout::UnmatchedTestSpec | WarnAbout::InfiniteGenerator ) );
}
}