From fe6e9ab99c12c9f7794113e57063e2ad52932d43 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Thu, 3 Jul 2025 21:06:49 +0000 Subject: [PATCH 1/7] feat: added early console print statements on missing or ambiguous steps --- .../features/test_ambiguous_steps.feature | 5 ++ .../features/test_scenarios.feature | 2 +- cucumber_cpp/acceptance_test/steps/Steps.cpp | 10 +++ cucumber_cpp/acceptance_test/test.bats | 9 ++ cucumber_cpp/library/Application.cpp | 2 +- cucumber_cpp/library/StepRegistry.cpp | 13 +-- cucumber_cpp/library/StepRegistry.hpp | 85 ++++++++++--------- .../library/engine/FeatureFactory.cpp | 11 ++- .../library/engine/FeatureFactory.hpp | 4 +- cucumber_cpp/library/engine/StepInfo.cpp | 6 +- cucumber_cpp/library/engine/StepInfo.hpp | 16 ++-- cucumber_cpp/library/engine/TestExecution.cpp | 8 +- cucumber_cpp/library/engine/TestExecution.hpp | 10 +-- .../engine/test/TestFeatureFactory.cpp | 8 +- .../library/engine/test/TestTestRunner.cpp | 5 +- cucumber_cpp/library/report/JunitReport.cpp | 10 +++ cucumber_cpp/library/report/JunitReport.hpp | 3 + cucumber_cpp/library/report/Report.cpp | 11 +++ cucumber_cpp/library/report/Report.hpp | 9 ++ cucumber_cpp/library/report/StdOutReport.cpp | 41 +++++++-- cucumber_cpp/library/report/StdOutReport.hpp | 3 + 21 files changed, 192 insertions(+), 79 deletions(-) create mode 100644 cucumber_cpp/acceptance_test/features/test_ambiguous_steps.feature diff --git a/cucumber_cpp/acceptance_test/features/test_ambiguous_steps.feature b/cucumber_cpp/acceptance_test/features/test_ambiguous_steps.feature new file mode 100644 index 00000000..35b83d40 --- /dev/null +++ b/cucumber_cpp/acceptance_test/features/test_ambiguous_steps.feature @@ -0,0 +1,5 @@ +@result:AMBIGUOUS +Feature: Test ambiguous steps + Scenario: Contains ambiguous steps + Given this is ambiguous + When a when step diff --git a/cucumber_cpp/acceptance_test/features/test_scenarios.feature b/cucumber_cpp/acceptance_test/features/test_scenarios.feature index a3007ba9..becfe99f 100644 --- a/cucumber_cpp/acceptance_test/features/test_scenarios.feature +++ b/cucumber_cpp/acceptance_test/features/test_scenarios.feature @@ -20,5 +20,5 @@ Feature: Simple feature file Scenario: A scenario with undefined step Given a given step When a when step - Then a when step + Then a missing step Then a then step diff --git a/cucumber_cpp/acceptance_test/steps/Steps.cpp b/cucumber_cpp/acceptance_test/steps/Steps.cpp index f7ca35f8..63ef7f3c 100644 --- a/cucumber_cpp/acceptance_test/steps/Steps.cpp +++ b/cucumber_cpp/acceptance_test/steps/Steps.cpp @@ -50,3 +50,13 @@ GIVEN("a ← tuple\\({float}, {float}, {float}, {float}\\)", (float /* unused */ { // empty } + +GIVEN("this is ambiguous") +{ + // empty +} + +GIVEN("this is ambiguous( or not)") +{ + // empty +} diff --git a/cucumber_cpp/acceptance_test/test.bats b/cucumber_cpp/acceptance_test/test.bats index 45fa3f93..72019d72 100644 --- a/cucumber_cpp/acceptance_test/test.bats +++ b/cucumber_cpp/acceptance_test/test.bats @@ -126,10 +126,19 @@ teardown() { @test "Dry run with known missing steps" { run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test run --feature cucumber_cpp/acceptance_test/features --tag "@result:UNDEFINED" --report console --dry assert_failure + assert_output --partial "Step missing: \"a missing step\"" assert_output --partial "undefined \"cucumber_cpp/acceptance_test/features/test_scenarios.feature\"" assert_output --partial "skipped Then a then step" } +@test "Dry run with known ambiguous steps" { + run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test run --feature cucumber_cpp/acceptance_test/features --tag "@result:AMBIGUOUS" --report console --dry + assert_failure + assert_output --partial "Ambiguous step: \"this is ambiguous\" Matches" + assert_output --partial "ambiguous \"cucumber_cpp/acceptance_test/features/test_ambiguous_steps.feature\"" + assert_output --partial "skipped When a when step" +} + @test "Test the and keyword" { run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test run --feature cucumber_cpp/acceptance_test/features --tag "@keyword-and" --report console assert_success diff --git a/cucumber_cpp/library/Application.cpp b/cucumber_cpp/library/Application.cpp index 5ddf5a93..5f6ae7d9 100644 --- a/cucumber_cpp/library/Application.cpp +++ b/cucumber_cpp/library/Application.cpp @@ -201,7 +201,7 @@ namespace cucumber_cpp::library engine::TestExecutionImpl testExecution{ contextManager, reporters, hookExecution, runPolicy }; StepRegistry stepRegistry{ parameterRegistry }; - engine::FeatureTreeFactory featureTreeFactory{ stepRegistry }; + engine::FeatureTreeFactory featureTreeFactory{ stepRegistry, reporters }; engine::TestRunnerImpl testRunner{ featureTreeFactory, testExecution }; diff --git a/cucumber_cpp/library/StepRegistry.cpp b/cucumber_cpp/library/StepRegistry.cpp index 01bb8858..d627bb71 100644 --- a/cucumber_cpp/library/StepRegistry.cpp +++ b/cucumber_cpp/library/StepRegistry.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -34,10 +35,10 @@ namespace cucumber_cpp::library : parameterRegistry{ parameterRegistry } { for (const auto& matcher : StepStringRegistration::Instance().GetEntries()) - Register(matcher.regex, matcher.type, matcher.factory); + Register(matcher.regex, matcher.type, matcher.factory, matcher.loc); } - StepMatch StepRegistry::Query(engine::StepType stepType, const std::string& expression) + StepRegistry::StepMatch StepRegistry::Query(engine::StepType stepType, const std::string& expression) { std::vector matches; @@ -46,7 +47,7 @@ namespace cucumber_cpp::library auto match = std::visit(cucumber_expression::MatchVisitor{ expression }, entry.regex); if (match) { - matches.emplace_back(entry.factory, *match, std::visit(cucumber_expression::PatternVisitor{}, entry.regex)); + matches.emplace_back(entry, entry.factory, *match, std::visit(cucumber_expression::PatternVisitor{}, entry.regex)); ++entry.used; } } @@ -82,12 +83,12 @@ namespace cucumber_cpp::library return list; } - void StepRegistry::Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr (&factory)(Context& context, const engine::Table& table)) + void StepRegistry::Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr (&factory)(Context& context, const engine::Table& table), std::source_location loc) { if (matcher.starts_with('^') || matcher.ends_with('$')) - registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type, matcher }, factory); + registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type, matcher }, factory, loc); else - registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type, matcher, parameterRegistry }, factory); + registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type, matcher, parameterRegistry }, factory, loc); } StepStringRegistration& StepStringRegistration::Instance() diff --git a/cucumber_cpp/library/StepRegistry.hpp b/cucumber_cpp/library/StepRegistry.hpp index 326d3f39..8612e4ed 100644 --- a/cucumber_cpp/library/StepRegistry.hpp +++ b/cucumber_cpp/library/StepRegistry.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -27,50 +28,40 @@ namespace cucumber_cpp::library return std::make_unique(context, table); } - struct StepMatch - { - StepMatch(std::unique_ptr (&factory)(Context& context, const engine::Table& table), std::variant, std::vector> matches, std::string_view stepRegexStr) - : factory(factory) - , matches(std::move(matches)) - , stepRegexStr(stepRegexStr) - {} - - std::unique_ptr (&factory)(Context& context, const engine::Table& table); - std::variant, std::vector> matches{}; - std::string_view stepRegexStr{}; - }; - struct StepRegistry { - struct StepNotFoundError : std::exception - { - using std::exception::exception; - }; - - struct AmbiguousStepError : std::exception - { - explicit AmbiguousStepError(std::vector&& matches) - : matches{ std::move(matches) } - {} - - std::vector matches; - }; - struct Entry { - Entry(engine::StepType type, cucumber_expression::Matcher regex, std::unique_ptr (&factory)(Context& context, const engine::Table& table)) - : type(type) - , regex(std::move(regex)) - , factory(factory) + Entry(engine::StepType type, cucumber_expression::Matcher regex, std::unique_ptr (&factory)(Context& context, const engine::Table& table), std::source_location loc) + : type{ type } + , regex{ std::move(regex) } + , factory{ factory } + , loc{ loc } {} engine::StepType type{}; cucumber_expression::Matcher regex; std::unique_ptr (&factory)(Context& context, const engine::Table& table); + std::source_location loc; std::uint32_t used{ 0 }; }; + struct StepMatch + { + StepMatch(Entry& entry, std::unique_ptr (&factory)(Context& context, const engine::Table& table), std::variant, std::vector> matches, std::string_view stepRegexStr) + : entry{ entry } + , factory{ factory } + , matches{ std::move(matches) } + , stepRegexStr{ stepRegexStr } + {} + + Entry& entry; + std::unique_ptr (&factory)(Context& context, const engine::Table& table); + std::variant, std::vector> matches{}; + std::string_view stepRegexStr{}; + }; + struct EntryView { EntryView(const cucumber_expression::Matcher& stepRegex, const std::uint32_t& used) @@ -82,6 +73,20 @@ namespace cucumber_cpp::library const std::uint32_t& used; }; + struct StepNotFoundError : std::exception + { + using std::exception::exception; + }; + + struct AmbiguousStepError : std::exception + { + explicit AmbiguousStepError(std::vector&& matches) + : matches{ std::move(matches) } + {} + + std::vector matches; + }; + explicit StepRegistry(cucumber_expression::ParameterRegistry& parameterRegistry); [[nodiscard]] StepMatch Query(engine::StepType stepType, const std::string& expression); @@ -92,7 +97,7 @@ namespace cucumber_cpp::library [[nodiscard]] std::vector List() const; private: - void Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr (&factory)(Context& context, const engine::Table& table)); + void Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr (&factory)(Context& context, const engine::Table& table), std::source_location loc); std::vector registry; cucumber_expression::ParameterRegistry& parameterRegistry; @@ -108,19 +113,21 @@ namespace cucumber_cpp::library struct Entry { - Entry(engine::StepType type, std::string regex, std::unique_ptr (&factory)(Context& context, const engine::Table& table)) - : type(type) - , regex(std::move(regex)) - , factory(factory) + Entry(engine::StepType type, std::string regex, std::unique_ptr (&factory)(Context& context, const engine::Table& table), std::source_location loc) + : type{ type } + , regex{ std::move(regex) } + , factory{ factory } + , loc{ loc } {} engine::StepType type{}; std::string regex; std::unique_ptr (&factory)(Context& context, const engine::Table& table); + std::source_location loc; }; template - static std::size_t Register(const std::string& matcher, engine::StepType stepType); + static std::size_t Register(const std::string& matcher, engine::StepType stepType, std::source_location loc = std::source_location::current()); std::span GetEntries(); [[nodiscard]] std::span GetEntries() const; @@ -134,9 +141,9 @@ namespace cucumber_cpp::library ////////////////////////// template - std::size_t StepStringRegistration::Register(const std::string& matcher, engine::StepType stepType) + std::size_t StepStringRegistration::Register(const std::string& matcher, engine::StepType stepType, std::source_location loc) { - Instance().registry.emplace_back(stepType, matcher, StepBodyFactory); + Instance().registry.emplace_back(stepType, matcher, StepBodyFactory, loc); return Instance().registry.size(); } diff --git a/cucumber_cpp/library/engine/FeatureFactory.cpp b/cucumber_cpp/library/engine/FeatureFactory.cpp index fadc7448..8078b152 100644 --- a/cucumber_cpp/library/engine/FeatureFactory.cpp +++ b/cucumber_cpp/library/engine/FeatureFactory.cpp @@ -284,8 +284,9 @@ namespace cucumber_cpp::library::engine } } - FeatureTreeFactory::FeatureTreeFactory(StepRegistry& stepRegistry) + FeatureTreeFactory::FeatureTreeFactory(StepRegistry& stepRegistry, report::ReportForwarder& reportHandler) : stepRegistry{ stepRegistry } + , reportHandler{ reportHandler } {} std::unique_ptr FeatureTreeFactory::CreateStepInfo(StepType stepType, std::string stepText, const ScenarioInfo& scenarioInfo, std::size_t line, std::size_t column, std::vector> table) const @@ -297,11 +298,15 @@ namespace cucumber_cpp::library::engine } catch (const StepRegistry::StepNotFoundError&) { - return std::make_unique(scenarioInfo, std::move(stepText), stepType, line, column, std::move(table)); + auto stepInfo = std::make_unique(scenarioInfo, stepText, stepType, line, column, std::move(table)); + reportHandler.StepMissing(stepText); + return stepInfo; } catch (StepRegistry::AmbiguousStepError& ase) { - return std::make_unique(scenarioInfo, std::move(stepText), stepType, line, column, std::move(table), std::move(ase.matches)); + auto stepInfo = std::make_unique(scenarioInfo, stepText, stepType, line, column, std::move(table), std::move(ase.matches)); + reportHandler.StepAmbiguous(stepText, *stepInfo); + return stepInfo; } } diff --git a/cucumber_cpp/library/engine/FeatureFactory.hpp b/cucumber_cpp/library/engine/FeatureFactory.hpp index 7e71d904..e381af92 100644 --- a/cucumber_cpp/library/engine/FeatureFactory.hpp +++ b/cucumber_cpp/library/engine/FeatureFactory.hpp @@ -7,6 +7,7 @@ #include "cucumber_cpp/library/engine/StepInfo.hpp" #include "cucumber_cpp/library/engine/StepType.hpp" #include "cucumber_cpp/library/engine/Table.hpp" +#include "cucumber_cpp/library/report/Report.hpp" #include #include #include @@ -18,7 +19,7 @@ namespace cucumber_cpp::library::engine { struct FeatureTreeFactory { - explicit FeatureTreeFactory(StepRegistry& stepRegistry); + FeatureTreeFactory(StepRegistry& stepRegistry, report::ReportForwarder& reportHandler); [[nodiscard]] std::unique_ptr CreateStepInfo(StepType stepType, std::string stepText, const ScenarioInfo& scenarioInfo, std::size_t line, std::size_t column, std::vector> table) const; @@ -26,6 +27,7 @@ namespace cucumber_cpp::library::engine private: StepRegistry& stepRegistry; + report::ReportForwarder& reportHandler; }; } diff --git a/cucumber_cpp/library/engine/StepInfo.cpp b/cucumber_cpp/library/engine/StepInfo.cpp index 29b8c29e..e92a276e 100644 --- a/cucumber_cpp/library/engine/StepInfo.cpp +++ b/cucumber_cpp/library/engine/StepInfo.cpp @@ -20,7 +20,7 @@ namespace cucumber_cpp::library::engine { } - StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, struct StepMatch stepMatch) + StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, StepRegistry::StepMatch stepMatch) : scenarioInfo{ scenarioInfo } , text{ std::move(text) } , type{ type } @@ -31,7 +31,7 @@ namespace cucumber_cpp::library::engine { } - StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, std::vector stepMatches) + StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, std::vector stepMatches) : scenarioInfo{ scenarioInfo } , text{ std::move(text) } , type{ type } @@ -72,7 +72,7 @@ namespace cucumber_cpp::library::engine return table; } - const std::variant>& StepInfo::StepMatch() const + const std::variant>& StepInfo::StepMatch() const { return stepMatch; } diff --git a/cucumber_cpp/library/engine/StepInfo.hpp b/cucumber_cpp/library/engine/StepInfo.hpp index d158ca0c..2ce9b4a7 100644 --- a/cucumber_cpp/library/engine/StepInfo.hpp +++ b/cucumber_cpp/library/engine/StepInfo.hpp @@ -17,8 +17,8 @@ namespace cucumber_cpp::library::engine struct StepInfo { StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table); - StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, StepMatch); - StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, std::vector); + StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, StepRegistry::StepMatch); + StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector> table, std::vector); [[nodiscard]] const struct ScenarioInfo& ScenarioInfo() const; @@ -29,7 +29,7 @@ namespace cucumber_cpp::library::engine [[nodiscard]] std::size_t Column() const; [[nodiscard]] const std::vector>& Table() const; - [[nodiscard]] const std::variant>& StepMatch() const; + [[nodiscard]] const std::variant>& StepMatch() const; private: const struct ScenarioInfo& scenarioInfo; @@ -41,14 +41,14 @@ namespace cucumber_cpp::library::engine std::size_t column; std::vector> table; - std::variant> stepMatch; + std::variant> stepMatch; }; struct NestedStepInfo { NestedStepInfo(std::string text, StepType type, std::filesystem::path, std::size_t line, std::size_t column, std::vector> table); - NestedStepInfo(std::string text, StepType type, std::filesystem::path, std::size_t line, std::size_t column, std::vector> table, StepMatch); - NestedStepInfo(std::string text, StepType type, std::filesystem::path, std::size_t line, std::size_t column, std::vector> table, std::vector); + NestedStepInfo(std::string text, StepType type, std::filesystem::path, std::size_t line, std::size_t column, std::vector> table, StepRegistry::StepMatch); + NestedStepInfo(std::string text, StepType type, std::filesystem::path, std::size_t line, std::size_t column, std::vector> table, std::vector); [[nodiscard]] const std::filesystem::path& FilePath() const; @@ -59,7 +59,7 @@ namespace cucumber_cpp::library::engine [[nodiscard]] std::size_t Column() const; [[nodiscard]] const std::vector>& Table() const; - [[nodiscard]] const std::variant>& StepMatch() const; + [[nodiscard]] const std::variant>& StepMatch() const; private: const std::filesystem::path filePath; @@ -71,7 +71,7 @@ namespace cucumber_cpp::library::engine std::size_t column; std::vector> table; - std::variant> stepMatch; + std::variant> stepMatch; }; } diff --git a/cucumber_cpp/library/engine/TestExecution.cpp b/cucumber_cpp/library/engine/TestExecution.cpp index d48b735c..631c76e0 100644 --- a/cucumber_cpp/library/engine/TestExecution.cpp +++ b/cucumber_cpp/library/engine/TestExecution.cpp @@ -53,12 +53,12 @@ namespace cucumber_cpp::library::engine { } - void DryRunPolicy::RunStep(cucumber_cpp::library::engine::ContextManager& /* contextManager */, const StepMatch& /* stepMatch */) const + void DryRunPolicy::RunStep(cucumber_cpp::library::engine::ContextManager& /* contextManager */, const StepRegistry::StepMatch& /* stepMatch */) const { /* don't execute actual steps */ } - void ExecuteRunPolicy::RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepMatch& stepMatch) const + void ExecuteRunPolicy::RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepRegistry::StepMatch& stepMatch) const { const auto& stepContext = contextManager.StepContext(); auto& scenarioContext = contextManager.ScenarioContext(); @@ -119,12 +119,12 @@ namespace cucumber_cpp::library::engine contextManager.StepContext().ExecutionStatus(cucumber_cpp::library::engine::Result::undefined); } - void TestExecutionImpl::RunStepMatch(const std::vector& /* not used */) + void TestExecutionImpl::RunStepMatch(const std::vector& /* not used */) { contextManager.StepContext().ExecutionStatus(cucumber_cpp::library::engine::Result::ambiguous); } - void TestExecutionImpl::RunStepMatch(const StepMatch& stepMatch) + void TestExecutionImpl::RunStepMatch(const StepRegistry::StepMatch& stepMatch) { executionPolicy.RunStep(contextManager, stepMatch); } diff --git a/cucumber_cpp/library/engine/TestExecution.hpp b/cucumber_cpp/library/engine/TestExecution.hpp index 0d541e60..4304d937 100644 --- a/cucumber_cpp/library/engine/TestExecution.hpp +++ b/cucumber_cpp/library/engine/TestExecution.hpp @@ -88,21 +88,21 @@ namespace cucumber_cpp::library::engine ~Policy() = default; public: - virtual void RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepMatch& stepMatch) const = 0; + virtual void RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepRegistry::StepMatch& stepMatch) const = 0; }; struct DryRunPolicy : TestExecution::Policy { virtual ~DryRunPolicy() = default; - void RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepMatch& stepMatch) const override; + void RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepRegistry::StepMatch& stepMatch) const override; }; struct ExecuteRunPolicy : TestExecution::Policy { virtual ~ExecuteRunPolicy() = default; - void RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepMatch& stepMatch) const override; + void RunStep(cucumber_cpp::library::engine::ContextManager& contextManager, const StepRegistry::StepMatch& stepMatch) const override; }; extern const DryRunPolicy dryRunPolicy; @@ -122,8 +122,8 @@ namespace cucumber_cpp::library::engine private: void RunStepMatch(std::monostate); - void RunStepMatch(const std::vector&); - void RunStepMatch(const StepMatch& stepMatch); + void RunStepMatch(const std::vector&); + void RunStepMatch(const StepRegistry::StepMatch& stepMatch); cucumber_cpp::library::engine::ContextManager& contextManager; report::ReportForwarder& reportHandler; diff --git a/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp b/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp index 65f4d16d..abaf0e63 100644 --- a/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp +++ b/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp @@ -2,7 +2,10 @@ #include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/engine/FeatureFactory.hpp" #include "cucumber_cpp/library/engine/StepType.hpp" +#include "cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp" #include "cucumber_cpp/library/engine/test_helper/TemporaryFile.hpp" +#include "cucumber_cpp/library/report/Report.hpp" +#include "cucumber_cpp/library/report/test_helper/ReportForwarderMock.hpp" #include #include #include @@ -17,7 +20,10 @@ namespace cucumber_cpp::library::engine { cucumber_expression::ParameterRegistry parameterRegistry; StepRegistry stepRegistry{ parameterRegistry }; - FeatureTreeFactory featureTreeFactory{ stepRegistry }; + + test_helper::ContextManagerInstance contextManager; + report::ReportForwarderImpl reportForwarderImpl{ contextManager }; + FeatureTreeFactory featureTreeFactory{ stepRegistry, reportForwarderImpl }; }; TEST_F(TestFeatureFactory, CreateEmptyFeature) diff --git a/cucumber_cpp/library/engine/test/TestTestRunner.cpp b/cucumber_cpp/library/engine/test/TestTestRunner.cpp index e630b755..4ea282ff 100644 --- a/cucumber_cpp/library/engine/test/TestTestRunner.cpp +++ b/cucumber_cpp/library/engine/test/TestTestRunner.cpp @@ -84,9 +84,10 @@ namespace cucumber_cpp::library::engine { cucumber_expression::ParameterRegistry parameterRegistry; StepRegistry stepRegistry{ parameterRegistry }; - FeatureTreeFactory featureTreeFactory{ stepRegistry }; - + report::ReportForwarderImpl reportForwarderImpl{ testExecutionMock.contextManager }; testing::StrictMock testExecutionMock; + FeatureTreeFactory featureTreeFactory{ stepRegistry, reportForwarderImpl }; + TestRunnerImpl runner{ featureTreeFactory, testExecutionMock }; std::vector> features; diff --git a/cucumber_cpp/library/report/JunitReport.cpp b/cucumber_cpp/library/report/JunitReport.cpp index 3b321169..e45d3646 100644 --- a/cucumber_cpp/library/report/JunitReport.cpp +++ b/cucumber_cpp/library/report/JunitReport.cpp @@ -170,6 +170,16 @@ namespace cucumber_cpp::library::report totalTime += duration; } + void JunitReport::StepMissing(const std::string& stepText) + { + /* do nothing */ + } + + void JunitReport::StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) + { + /* do nothing */ + } + void JunitReport::StepSkipped(const engine::StepInfo& stepInfo) { /* do nothing */ diff --git a/cucumber_cpp/library/report/JunitReport.hpp b/cucumber_cpp/library/report/JunitReport.hpp index 8c528f4e..9b294984 100644 --- a/cucumber_cpp/library/report/JunitReport.hpp +++ b/cucumber_cpp/library/report/JunitReport.hpp @@ -30,6 +30,9 @@ namespace cucumber_cpp::library::report void ScenarioStart(const engine::ScenarioInfo& scenarioInfo) override; void ScenarioEnd(engine::Result result, const engine::ScenarioInfo& scenarioInfo, TraceTime::Duration duration) override; + void StepMissing(const std::string& stepText) override; + void StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) override; + void StepSkipped(const engine::StepInfo& stepInfo) override; void StepStart(const engine::StepInfo& stepInfo) override; void StepEnd(engine::Result result, const engine::StepInfo& stepInfo, TraceTime::Duration duration) override; diff --git a/cucumber_cpp/library/report/Report.cpp b/cucumber_cpp/library/report/Report.cpp index 3ca74a18..6d4049bc 100644 --- a/cucumber_cpp/library/report/Report.cpp +++ b/cucumber_cpp/library/report/Report.cpp @@ -1,6 +1,7 @@ #include "cucumber_cpp/library/report/Report.hpp" #include "cucumber_cpp/library/TraceTime.hpp" #include "cucumber_cpp/library/engine/ContextManager.hpp" +#include "cucumber_cpp/library/engine/StepInfo.hpp" #include #include #include @@ -140,6 +141,16 @@ namespace cucumber_cpp::library::report return StepScope{ contextManager.StepContext(), Storage() }; } + void ReportForwarderImpl::StepMissing(const std::string& stepText) + { + ForwardCall(Storage(), &ReportHandlerV2::StepMissing, stepText); + } + + void ReportForwarderImpl::StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) + { + ForwardCall(Storage(), &ReportHandlerV2::StepAmbiguous, stepText, stepInfo); + } + void ReportForwarderImpl::StepSkipped() { ForwardCall(Storage(), &ReportHandlerV2::StepSkipped, contextManager.StepContext().info); diff --git a/cucumber_cpp/library/report/Report.hpp b/cucumber_cpp/library/report/Report.hpp index b5dc613d..56dbf418 100644 --- a/cucumber_cpp/library/report/Report.hpp +++ b/cucumber_cpp/library/report/Report.hpp @@ -39,6 +39,9 @@ namespace cucumber_cpp::library::report virtual void ScenarioStart(const engine::ScenarioInfo& scenarioInfo) = 0; virtual void ScenarioEnd(engine::Result result, const engine::ScenarioInfo& scenarioInfo, TraceTime::Duration duration) = 0; + virtual void StepMissing(const std::string& stepText) = 0; + virtual void StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) = 0; + virtual void StepSkipped(const engine::StepInfo& stepInfo) = 0; virtual void StepStart(const engine::StepInfo& stepInfo) = 0; virtual void StepEnd(engine::Result result, const engine::StepInfo& stepInfo, TraceTime::Duration duration) = 0; @@ -85,6 +88,9 @@ namespace cucumber_cpp::library::report [[nodiscard]] virtual ScenarioScope ScenarioStart() = 0; [[nodiscard]] virtual StepScope StepStart() = 0; + virtual void StepMissing(const std::string& stepText) = 0; + virtual void StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) = 0; + virtual void StepSkipped() = 0; virtual void Failure(const std::string& error, std::optional path = {}, std::optional line = {}, std::optional column = {}) = 0; @@ -158,6 +164,9 @@ namespace cucumber_cpp::library::report [[nodiscard]] ScenarioScope ScenarioStart() override; [[nodiscard]] StepScope StepStart() override; + void StepMissing(const std::string& stepText) override; + void StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) override; + void StepSkipped() override; void Failure(const std::string& error, std::optional path = {}, std::optional line = {}, std::optional column = {}) override; diff --git a/cucumber_cpp/library/report/StdOutReport.cpp b/cucumber_cpp/library/report/StdOutReport.cpp index f17c17d5..1aeac889 100644 --- a/cucumber_cpp/library/report/StdOutReport.cpp +++ b/cucumber_cpp/library/report/StdOutReport.cpp @@ -1,4 +1,5 @@ #include "cucumber_cpp/library/report/StdOutReport.hpp" +#include "cucumber_cpp/library/StepRegistry.hpp" #include "cucumber_cpp/library/TraceTime.hpp" #include "cucumber_cpp/library/engine/FeatureInfo.hpp" #include "cucumber_cpp/library/engine/Result.hpp" @@ -17,6 +18,8 @@ #include #include #include +#include +#include #ifdef _MSC_VER // clang-format off @@ -34,27 +37,32 @@ namespace cucumber_cpp::library::report namespace { #ifndef _MSC_VER + constexpr auto redStr{ "\o{33}[1m\o{33}[31m" }; + constexpr auto greenStr{ "\o{33}[1m\o{33}[32m" }; + constexpr auto cyanStr{ "\o{33}[1m\o{33}[36m" }; + constexpr auto defaultStr{ "\o{33}[0m\o{33}[39m" }; + inline std::ostream& TcRed(std::ostream& o) { - o << "\o{33}[1m\o{33}[31m"; + o << redStr; return o; } inline std::ostream& TcGreen(std::ostream& o) { - o << "\o{33}[1m\o{33}[32m"; + o << greenStr; return o; } inline std::ostream& TcCyan(std::ostream& o) { - o << "\o{33}[1m\o{33}[36m"; + o << cyanStr; return o; } inline std::ostream& TcDefault(std::ostream& o) { - o << "\o{33}[0m\o{33}[39m"; + o << defaultStr; return o; } #else @@ -184,7 +192,6 @@ namespace cucumber_cpp::library::report void StdOutReport::ScenarioEnd(engine::Result result, const engine::ScenarioInfo& scenarioInfo, TraceTime::Duration duration) { - using enum engine::Result; std::cout << "\n" @@ -202,6 +209,30 @@ namespace cucumber_cpp::library::report } } + void StdOutReport::StepMissing(const std::string& stepText) + { + std::cout << "\n" + << std::format(R"({}Step missing: "{}")", redStr, stepText); + std::cout << TcDefault; + } + + void StdOutReport::StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) + { + std::cout << "\n" + << std::format(R"({}Ambiguous step: "{}" Matches:)", redStr, stepText); + + for (const auto& match : std::get>(stepInfo.StepMatch())) + { + std::visit([&match](const auto& pattern) + { + std::cout << "\n" + << std::format(R"({}:{}:{} : {})", match.entry.loc.file_name(), match.entry.loc.line(), match.entry.loc.column(), pattern.Source()); + }, + match.entry.regex); + } + std::cout << TcDefault; + } + void StdOutReport::StepSkipped(const engine::StepInfo& stepInfo) { std::cout << "\n" diff --git a/cucumber_cpp/library/report/StdOutReport.hpp b/cucumber_cpp/library/report/StdOutReport.hpp index ab392b3e..fa8d5aae 100644 --- a/cucumber_cpp/library/report/StdOutReport.hpp +++ b/cucumber_cpp/library/report/StdOutReport.hpp @@ -30,6 +30,9 @@ namespace cucumber_cpp::library::report void ScenarioStart(const engine::ScenarioInfo& scenarioInfo) override; void ScenarioEnd(engine::Result result, const engine::ScenarioInfo& scenarioInfo, TraceTime::Duration duration) override; + void StepMissing(const std::string& stepText) override; + void StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) override; + void StepSkipped(const engine::StepInfo& stepInfo) override; void StepStart(const engine::StepInfo& stepInfo) override; void StepEnd(engine::Result result, const engine::StepInfo& stepInfo, TraceTime::Duration duration) override; From 3144b2f9892ae0d50ddf299139dfb92fba13120e Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 4 Jul 2025 13:18:43 +0000 Subject: [PATCH 2/7] feat: add test suite for StdOutReport and improve debugging configurations --- .vscode/launch.json | 18 ++ .../test_helper/ContextManagerInstance.hpp | 10 +- cucumber_cpp/library/report/CMakeLists.txt | 1 + cucumber_cpp/library/report/StdOutReport.cpp | 3 +- .../library/report/test/CMakeLists.txt | 14 + .../library/report/test/TestStdOutReport.cpp | 267 ++++++++++++++++++ 6 files changed, 306 insertions(+), 7 deletions(-) create mode 100644 cucumber_cpp/library/report/test/CMakeLists.txt create mode 100644 cucumber_cpp/library/report/test/TestStdOutReport.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index b6c90e28..4328a992 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,6 +28,24 @@ } ], "configurations": [ + { + "name": "Debug Test", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, { "name": "(gdb) Launch", "type": "cppdbg", diff --git a/cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp b/cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp index 6950a5fa..f09f66c1 100644 --- a/cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp +++ b/cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp @@ -23,12 +23,12 @@ namespace cucumber_cpp::library::engine::test_helper struct ContextManagerInstance : private ContextManagerInstanceStorage , cucumber_cpp::library::engine::ContextManager { - ContextManagerInstance(std::set> tags = {}) + explicit ContextManagerInstance(std::set> tags = {}) : cucumber_cpp::library::engine::ContextManager{ contextStorageFactory } - , feature{ tags, {}, {}, {}, {}, {} } - , rule{ feature, {}, {}, {}, {}, {} } - , scenario{ rule, tags, {}, {}, {}, {} } - , step{ scenario, {}, {}, {}, {}, {} } + , feature{ tags, "FeatureInfo", {}, {}, {}, {} } + , rule{ feature, {}, "RuleInfo", {}, {}, {} } + , scenario{ rule, tags, "ScenarioInfo", {}, {}, {} } + , step{ scenario, "StepInfo", {}, {}, {}, {} } { } diff --git a/cucumber_cpp/library/report/CMakeLists.txt b/cucumber_cpp/library/report/CMakeLists.txt index bba15987..3ad02aed 100644 --- a/cucumber_cpp/library/report/CMakeLists.txt +++ b/cucumber_cpp/library/report/CMakeLists.txt @@ -19,5 +19,6 @@ target_link_libraries(cucumber_cpp.library.report PUBLIC ) if (CCR_BUILD_TESTS) + add_subdirectory(test) add_subdirectory(test_helper) endif() diff --git a/cucumber_cpp/library/report/StdOutReport.cpp b/cucumber_cpp/library/report/StdOutReport.cpp index 1aeac889..ea24dc39 100644 --- a/cucumber_cpp/library/report/StdOutReport.cpp +++ b/cucumber_cpp/library/report/StdOutReport.cpp @@ -174,8 +174,7 @@ namespace cucumber_cpp::library::report void StdOutReport::RuleStart(const engine::RuleInfo& ruleInfo) { - std::cout << "\n" - << ruleInfo.Title(); + // not required } void StdOutReport::RuleEnd(engine::Result result, const engine::RuleInfo& ruleInfo, TraceTime::Duration duration) diff --git a/cucumber_cpp/library/report/test/CMakeLists.txt b/cucumber_cpp/library/report/test/CMakeLists.txt new file mode 100644 index 00000000..63e174fa --- /dev/null +++ b/cucumber_cpp/library/report/test/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(cucumber_cpp.library.report.test) +add_test(NAME cucumber_cpp.library.report.test COMMAND cucumber_cpp.library.report.test) + +target_link_libraries(cucumber_cpp.library.report.test PUBLIC + GTest::gtest + GTest::gmock + GTest::gmock_main + + cucumber_cpp.library.report +) + +target_sources(cucumber_cpp.library.report.test PRIVATE + TestStdOutReport.cpp +) diff --git a/cucumber_cpp/library/report/test/TestStdOutReport.cpp b/cucumber_cpp/library/report/test/TestStdOutReport.cpp new file mode 100644 index 00000000..eae74fb8 --- /dev/null +++ b/cucumber_cpp/library/report/test/TestStdOutReport.cpp @@ -0,0 +1,267 @@ +#include "cucumber_cpp/library/TraceTime.hpp" +#include "cucumber_cpp/library/engine/Result.hpp" +#include "cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp" +#include "cucumber_cpp/library/report/StdOutReport.hpp" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +namespace cucumber_cpp::library::report +{ + struct TestStdOutReport : testing::Test + { + engine::test_helper::ContextManagerInstance contextManagerInstance; + StdOutReport stdOutReport; + }; + + TEST_F(TestStdOutReport, FeatureStartPrintNothing) + { + testing::internal::CaptureStdout(); + stdOutReport.FeatureStart(contextManagerInstance.FeatureContext().info); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, FeatureEndPrintNothing) + { + testing::internal::CaptureStdout(); + stdOutReport.FeatureEnd({}, contextManagerInstance.FeatureContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, RuleStart) + { + testing::internal::CaptureStdout(); + stdOutReport.RuleStart(contextManagerInstance.RuleContext().info); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, RuleEndPrintNothing) + { + testing::internal::CaptureStdout(); + stdOutReport.RuleEnd({}, contextManagerInstance.RuleContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, ScenarioStart) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioStart(contextManagerInstance.ScenarioContext().info); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("\nScenarioInfo")); + } + + TEST_F(TestStdOutReport, ScenarioEndPassed) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::passed, contextManagerInstance.ScenarioContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); + EXPECT_THAT(stdout, testing::HasSubstr("done (0ns)")); + } + + TEST_F(TestStdOutReport, ScenarioEndPassedTimeScale) + { + auto testDuration = [this](TraceTime::Duration duration, const char* time) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::passed, contextManagerInstance.ScenarioContext().info, duration); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr(time)); + }; + + testDuration(std::chrono::nanoseconds{ 1 }, "1ns"); + testDuration(std::chrono::microseconds{ 2 }, "2us"); + testDuration(std::chrono::milliseconds{ 3 }, "3ms"); + testDuration(std::chrono::seconds{ 4 }, "4s"); + testDuration(std::chrono::minutes{ 5 }, "5m"); + testDuration(std::chrono::hours{ 6 }, "6h"); + } + + TEST_F(TestStdOutReport, ScenarioEndSkipped) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::skipped, contextManagerInstance.ScenarioContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); + EXPECT_THAT(stdout, testing::HasSubstr("skipped (0ns)")); + } + + TEST_F(TestStdOutReport, ScenarioEndPending) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::pending, contextManagerInstance.ScenarioContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); + EXPECT_THAT(stdout, testing::HasSubstr("pending (0ns)")); + } + + TEST_F(TestStdOutReport, ScenarioEndUndefined) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::undefined, contextManagerInstance.ScenarioContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); + EXPECT_THAT(stdout, testing::HasSubstr("undefined (0ns)")); + } + + TEST_F(TestStdOutReport, ScenarioEndAmbiguous) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::ambiguous, contextManagerInstance.ScenarioContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); + EXPECT_THAT(stdout, testing::HasSubstr("ambiguous (0ns)")); + } + + TEST_F(TestStdOutReport, ScenarioEndFailed) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioEnd(engine::Result::failed, contextManagerInstance.ScenarioContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); + EXPECT_THAT(stdout, testing::HasSubstr("failed (0ns)")); + } + + TEST_F(TestStdOutReport, StepMissing) + { + testing::internal::CaptureStdout(); + stdOutReport.StepMissing("missing step text"); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("Step missing: \"missing step text\"")); + } + + TEST_F(TestStdOutReport, StepAmbiguous) + { + testing::internal::CaptureStdout(); + // stdOutReport.StepAmbiguous(); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, StepSkipped) + { + testing::internal::CaptureStdout(); + stdOutReport.StepSkipped(contextManagerInstance.StepContext().info); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n| ")); + EXPECT_THAT(stdout, testing::HasSubstr("skipped Given StepInfo")); + } + + TEST_F(TestStdOutReport, StepStart) + { + testing::internal::CaptureStdout(); + stdOutReport.StepStart(contextManagerInstance.StepContext().info); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("\n| Given StepInfo")); + } + + TEST_F(TestStdOutReport, NestedStepStart) + { + testing::internal::CaptureStdout(); + stdOutReport.StepStart(contextManagerInstance.StepContext().info); + testing::internal::GetCapturedStdout(); + + testing::internal::CaptureStdout(); + stdOutReport.StepStart(contextManagerInstance.StepContext().info); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("\n| | Given StepInfo")); + } + + TEST_F(TestStdOutReport, StepEnd) + { + testing::internal::CaptureStdout(); + stdOutReport.StepStart(contextManagerInstance.StepContext().info); + testing::internal::GetCapturedStdout(); + + testing::internal::CaptureStdout(); + stdOutReport.StepEnd(engine::Result::passed, contextManagerInstance.StepContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n| \\-> ")); + EXPECT_THAT(stdout, testing::HasSubstr("done (0ns)")); + } + + TEST_F(TestStdOutReport, StepEndNested) + { + testing::internal::CaptureStdout(); + stdOutReport.StepStart(contextManagerInstance.StepContext().info); + stdOutReport.StepStart(contextManagerInstance.StepContext().info); + testing::internal::GetCapturedStdout(); + + testing::internal::CaptureStdout(); + stdOutReport.StepEnd(engine::Result::passed, contextManagerInstance.StepContext().info, {}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\n| | \\-> ")); + EXPECT_THAT(stdout, testing::HasSubstr("done (0ns)")); + } + + TEST_F(TestStdOutReport, Failure) + { + testing::internal::CaptureStdout(); + stdOutReport.Failure("My Failure", "file.cpp", 1, 1); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\nFailure @ ./file.cpp:1:1:\nMy Failure")); + } + + TEST_F(TestStdOutReport, FailureWithoutColumn) + { + testing::internal::CaptureStdout(); + stdOutReport.Failure("My Failure", "file.cpp", 1); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\nFailure @ ./file.cpp:1:\nMy Failure")); + } + + TEST_F(TestStdOutReport, Error) + { + testing::internal::CaptureStdout(); + stdOutReport.Error("My Error", "file.cpp", 1, 1); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\nError @ ./file.cpp:1:1:\nMy Error")); + } + + TEST_F(TestStdOutReport, ErrorWithoutColumn) + { + testing::internal::CaptureStdout(); + stdOutReport.Error("My Error", "file.cpp", 1); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::HasSubstr("\nError @ ./file.cpp:1:\nMy Error")); + } + + TEST_F(TestStdOutReport, Trace) + { + testing::internal::CaptureStdout(); + stdOutReport.Trace("Traces a message without any formatting"); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("Traces a message without any formatting")); + } + + TEST_F(TestStdOutReport, Summary) + { + testing::internal::CaptureStdout(); + stdOutReport.ScenarioStart(contextManagerInstance.ScenarioContext().info); + stdOutReport.ScenarioStart(contextManagerInstance.ScenarioContext().info); + stdOutReport.ScenarioStart(contextManagerInstance.ScenarioContext().info); + + stdOutReport.ScenarioEnd(engine::Result::passed, contextManagerInstance.ScenarioContext().info, {}); + stdOutReport.ScenarioEnd(engine::Result::skipped, contextManagerInstance.ScenarioContext().info, {}); + stdOutReport.ScenarioEnd(engine::Result::failed, contextManagerInstance.ScenarioContext().info, {}); + + testing::internal::GetCapturedStdout(); + + testing::internal::CaptureStdout(); + stdOutReport.Summary({}); + const auto stdout{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(stdout, testing::StrEq("\n====================summary====================" + "\nduration: 0ns" + "\ntests : 1/3 passed" + "\n" + "\nfailed tests:" + "\n\"\":0:0 : \"ScenarioInfo\"" + "\n\"\":0:0 : \"ScenarioInfo\"")); + } + +} From addc50e2b24aaf504248decf5e6e5cb8bdecec75 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 4 Jul 2025 13:33:31 +0000 Subject: [PATCH 3/7] refactor: simplify color output handling in StdOutReport --- cucumber_cpp/library/report/StdOutReport.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/cucumber_cpp/library/report/StdOutReport.cpp b/cucumber_cpp/library/report/StdOutReport.cpp index ea24dc39..089d40a5 100644 --- a/cucumber_cpp/library/report/StdOutReport.cpp +++ b/cucumber_cpp/library/report/StdOutReport.cpp @@ -37,32 +37,27 @@ namespace cucumber_cpp::library::report namespace { #ifndef _MSC_VER - constexpr auto redStr{ "\o{33}[1m\o{33}[31m" }; - constexpr auto greenStr{ "\o{33}[1m\o{33}[32m" }; - constexpr auto cyanStr{ "\o{33}[1m\o{33}[36m" }; - constexpr auto defaultStr{ "\o{33}[0m\o{33}[39m" }; - inline std::ostream& TcRed(std::ostream& o) { - o << redStr; + o << "\o{33}[1m\o{33}[31m"; return o; } inline std::ostream& TcGreen(std::ostream& o) { - o << greenStr; + o << "\o{33}[1m\o{33}[32m"; return o; } inline std::ostream& TcCyan(std::ostream& o) { - o << cyanStr; + o << "\o{33}[1m\o{33}[36m"; return o; } inline std::ostream& TcDefault(std::ostream& o) { - o << defaultStr; + o << "\o{33}[0m\o{33}[39m"; return o; } #else @@ -211,14 +206,16 @@ namespace cucumber_cpp::library::report void StdOutReport::StepMissing(const std::string& stepText) { std::cout << "\n" - << std::format(R"({}Step missing: "{}")", redStr, stepText); + << TcRed + << std::format(R"(Step missing: "{}")", stepText); std::cout << TcDefault; } void StdOutReport::StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) { std::cout << "\n" - << std::format(R"({}Ambiguous step: "{}" Matches:)", redStr, stepText); + << TcRed + << std::format(R"(Ambiguous step: "{}" Matches:)", stepText); for (const auto& match : std::get>(stepInfo.StepMatch())) { From 8aae36247e0fa89bfac946ec6a99bcd04d674f1a Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 4 Jul 2025 13:46:38 +0000 Subject: [PATCH 4/7] fix msvc build --- .../library/report/test/TestStdOutReport.cpp | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/cucumber_cpp/library/report/test/TestStdOutReport.cpp b/cucumber_cpp/library/report/test/TestStdOutReport.cpp index eae74fb8..8e6721c9 100644 --- a/cucumber_cpp/library/report/test/TestStdOutReport.cpp +++ b/cucumber_cpp/library/report/test/TestStdOutReport.cpp @@ -19,49 +19,49 @@ namespace cucumber_cpp::library::report { testing::internal::CaptureStdout(); stdOutReport.FeatureStart(contextManagerInstance.FeatureContext().info); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); } TEST_F(TestStdOutReport, FeatureEndPrintNothing) { testing::internal::CaptureStdout(); stdOutReport.FeatureEnd({}, contextManagerInstance.FeatureContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); } TEST_F(TestStdOutReport, RuleStart) { testing::internal::CaptureStdout(); stdOutReport.RuleStart(contextManagerInstance.RuleContext().info); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); } TEST_F(TestStdOutReport, RuleEndPrintNothing) { testing::internal::CaptureStdout(); stdOutReport.RuleEnd({}, contextManagerInstance.RuleContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); } TEST_F(TestStdOutReport, ScenarioStart) { testing::internal::CaptureStdout(); stdOutReport.ScenarioStart(contextManagerInstance.ScenarioContext().info); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("\nScenarioInfo")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("\nScenarioInfo")); } TEST_F(TestStdOutReport, ScenarioEndPassed) { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::passed, contextManagerInstance.ScenarioContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); - EXPECT_THAT(stdout, testing::HasSubstr("done (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, testing::HasSubstr("done (0ns)")); } TEST_F(TestStdOutReport, ScenarioEndPassedTimeScale) @@ -70,8 +70,8 @@ namespace cucumber_cpp::library::report { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::passed, contextManagerInstance.ScenarioContext().info, duration); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr(time)); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr(time)); }; testDuration(std::chrono::nanoseconds{ 1 }, "1ns"); @@ -86,78 +86,78 @@ namespace cucumber_cpp::library::report { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::skipped, contextManagerInstance.ScenarioContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); - EXPECT_THAT(stdout, testing::HasSubstr("skipped (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, testing::HasSubstr("skipped (0ns)")); } TEST_F(TestStdOutReport, ScenarioEndPending) { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::pending, contextManagerInstance.ScenarioContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); - EXPECT_THAT(stdout, testing::HasSubstr("pending (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, testing::HasSubstr("pending (0ns)")); } TEST_F(TestStdOutReport, ScenarioEndUndefined) { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::undefined, contextManagerInstance.ScenarioContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); - EXPECT_THAT(stdout, testing::HasSubstr("undefined (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, testing::HasSubstr("undefined (0ns)")); } TEST_F(TestStdOutReport, ScenarioEndAmbiguous) { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::ambiguous, contextManagerInstance.ScenarioContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); - EXPECT_THAT(stdout, testing::HasSubstr("ambiguous (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, testing::HasSubstr("ambiguous (0ns)")); } TEST_F(TestStdOutReport, ScenarioEndFailed) { testing::internal::CaptureStdout(); stdOutReport.ScenarioEnd(engine::Result::failed, contextManagerInstance.ScenarioContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n\\->")); - EXPECT_THAT(stdout, testing::HasSubstr("failed (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, testing::HasSubstr("failed (0ns)")); } TEST_F(TestStdOutReport, StepMissing) { testing::internal::CaptureStdout(); stdOutReport.StepMissing("missing step text"); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("Step missing: \"missing step text\"")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("Step missing: \"missing step text\"")); } TEST_F(TestStdOutReport, StepAmbiguous) { testing::internal::CaptureStdout(); // stdOutReport.StepAmbiguous(); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); } TEST_F(TestStdOutReport, StepSkipped) { testing::internal::CaptureStdout(); stdOutReport.StepSkipped(contextManagerInstance.StepContext().info); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n| ")); - EXPECT_THAT(stdout, testing::HasSubstr("skipped Given StepInfo")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n| ")); + EXPECT_THAT(capture, testing::HasSubstr("skipped Given StepInfo")); } TEST_F(TestStdOutReport, StepStart) { testing::internal::CaptureStdout(); stdOutReport.StepStart(contextManagerInstance.StepContext().info); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("\n| Given StepInfo")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("\n| Given StepInfo")); } TEST_F(TestStdOutReport, NestedStepStart) @@ -168,8 +168,8 @@ namespace cucumber_cpp::library::report testing::internal::CaptureStdout(); stdOutReport.StepStart(contextManagerInstance.StepContext().info); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("\n| | Given StepInfo")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("\n| | Given StepInfo")); } TEST_F(TestStdOutReport, StepEnd) @@ -180,9 +180,9 @@ namespace cucumber_cpp::library::report testing::internal::CaptureStdout(); stdOutReport.StepEnd(engine::Result::passed, contextManagerInstance.StepContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n| \\-> ")); - EXPECT_THAT(stdout, testing::HasSubstr("done (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n| \\-> ")); + EXPECT_THAT(capture, testing::HasSubstr("done (0ns)")); } TEST_F(TestStdOutReport, StepEndNested) @@ -194,49 +194,49 @@ namespace cucumber_cpp::library::report testing::internal::CaptureStdout(); stdOutReport.StepEnd(engine::Result::passed, contextManagerInstance.StepContext().info, {}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\n| | \\-> ")); - EXPECT_THAT(stdout, testing::HasSubstr("done (0ns)")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n| | \\-> ")); + EXPECT_THAT(capture, testing::HasSubstr("done (0ns)")); } TEST_F(TestStdOutReport, Failure) { testing::internal::CaptureStdout(); stdOutReport.Failure("My Failure", "file.cpp", 1, 1); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\nFailure @ ./file.cpp:1:1:\nMy Failure")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\nFailure @ ./file.cpp:1:1:\nMy Failure")); } TEST_F(TestStdOutReport, FailureWithoutColumn) { testing::internal::CaptureStdout(); stdOutReport.Failure("My Failure", "file.cpp", 1); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\nFailure @ ./file.cpp:1:\nMy Failure")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\nFailure @ ./file.cpp:1:\nMy Failure")); } TEST_F(TestStdOutReport, Error) { testing::internal::CaptureStdout(); stdOutReport.Error("My Error", "file.cpp", 1, 1); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\nError @ ./file.cpp:1:1:\nMy Error")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\nError @ ./file.cpp:1:1:\nMy Error")); } TEST_F(TestStdOutReport, ErrorWithoutColumn) { testing::internal::CaptureStdout(); stdOutReport.Error("My Error", "file.cpp", 1); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::HasSubstr("\nError @ ./file.cpp:1:\nMy Error")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\nError @ ./file.cpp:1:\nMy Error")); } TEST_F(TestStdOutReport, Trace) { testing::internal::CaptureStdout(); stdOutReport.Trace("Traces a message without any formatting"); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("Traces a message without any formatting")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("Traces a message without any formatting")); } TEST_F(TestStdOutReport, Summary) @@ -254,14 +254,14 @@ namespace cucumber_cpp::library::report testing::internal::CaptureStdout(); stdOutReport.Summary({}); - const auto stdout{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(stdout, testing::StrEq("\n====================summary====================" - "\nduration: 0ns" - "\ntests : 1/3 passed" - "\n" - "\nfailed tests:" - "\n\"\":0:0 : \"ScenarioInfo\"" - "\n\"\":0:0 : \"ScenarioInfo\"")); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("\n====================summary====================" + "\nduration: 0ns" + "\ntests : 1/3 passed" + "\n" + "\nfailed tests:" + "\n\"\":0:0 : \"ScenarioInfo\"" + "\n\"\":0:0 : \"ScenarioInfo\"")); } } From 41dda564d77ea20f06ba5d91d806e9683fe51268 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 4 Jul 2025 13:47:35 +0000 Subject: [PATCH 5/7] test: add tests for missing and ambiguous step reporting from Feature Factory --- .../engine/test/TestFeatureFactory.cpp | 28 ++++++++++++++++++- .../test_helper/StepImplementations.cpp | 10 +++++++ .../test_helper/ReportForwarderMock.hpp | 4 +++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp b/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp index abaf0e63..171b5f4f 100644 --- a/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp +++ b/cucumber_cpp/library/engine/test/TestFeatureFactory.cpp @@ -22,7 +22,7 @@ namespace cucumber_cpp::library::engine StepRegistry stepRegistry{ parameterRegistry }; test_helper::ContextManagerInstance contextManager; - report::ReportForwarderImpl reportForwarderImpl{ contextManager }; + report::test_helper::ReportForwarderMock reportForwarderImpl{ contextManager }; FeatureTreeFactory featureTreeFactory{ stepRegistry, reportForwarderImpl }; }; @@ -315,4 +315,30 @@ namespace cucumber_cpp::library::engine EXPECT_THAT(scenario1->Children()[0]->Table()[1][0].As(), testing::StrEq("c")); EXPECT_THAT(scenario1->Children()[0]->Table()[1][1].As(), testing::StrEq("d")); } + + TEST_F(TestFeatureFactory, MissingStepsAreReported) + { + auto tmp = test_helper::TemporaryFile{ "tmpfile.feature" }; + + tmp << "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given this is a missing step\n"; + + EXPECT_CALL(reportForwarderImpl, StepMissing); + + const auto feature = featureTreeFactory.Create(tmp.Path(), ""); + } + + TEST_F(TestFeatureFactory, AmbiguousStepsAreReported) + { + auto tmp = test_helper::TemporaryFile{ "tmpfile.feature" }; + + tmp << "Feature: Test feature\n" + " Scenario: Test scenario\n" + " Given this is ambiguous\n"; + + EXPECT_CALL(reportForwarderImpl, StepAmbiguous); + + const auto feature = featureTreeFactory.Create(tmp.Path(), ""); + } } diff --git a/cucumber_cpp/library/engine/test_helper/StepImplementations.cpp b/cucumber_cpp/library/engine/test_helper/StepImplementations.cpp index 11ab5d6e..3422f877 100644 --- a/cucumber_cpp/library/engine/test_helper/StepImplementations.cpp +++ b/cucumber_cpp/library/engine/test_helper/StepImplementations.cpp @@ -110,3 +110,13 @@ THEN(R"(An expression with \\\{escaped braces\\} should keep the slash)") { /* do nothing */ } + +GIVEN("this is ambiguous") +{ + // empty +} + +GIVEN("this is ambiguous( or not)") +{ + // empty +} diff --git a/cucumber_cpp/library/report/test_helper/ReportForwarderMock.hpp b/cucumber_cpp/library/report/test_helper/ReportForwarderMock.hpp index 8451dbfc..04705050 100644 --- a/cucumber_cpp/library/report/test_helper/ReportForwarderMock.hpp +++ b/cucumber_cpp/library/report/test_helper/ReportForwarderMock.hpp @@ -1,6 +1,7 @@ #ifndef TEST_HELPER_REPORTFORWARDERMOCK_HPP #define TEST_HELPER_REPORTFORWARDERMOCK_HPP +#include "cucumber_cpp/library/engine/StepInfo.hpp" #include "cucumber_cpp/library/report/Report.hpp" #include #include @@ -15,6 +16,9 @@ namespace cucumber_cpp::library::report::test_helper using ReportForwarderImpl::ReportForwarderImpl; virtual ~ReportForwarderMock() = default; + MOCK_METHOD(void, StepMissing, (const std::string& stepText), (override)); + MOCK_METHOD(void, StepAmbiguous, (const std::string& stepText, const engine::StepInfo& stepInfo), (override)); + MOCK_METHOD(void, Failure, (const std::string& error, std::optional path, std::optional line, std::optional column), (override)); MOCK_METHOD(void, Error, (const std::string& error, std::optional path, std::optional line, std::optional column), (override)); }; From 3e35c086d9d715e666f4b802cec2e700ee9865db Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 4 Jul 2025 13:48:57 +0000 Subject: [PATCH 6/7] refactor: clean up formatting in launch.json for better readability --- .vscode/launch.json | 147 ++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 75 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4328a992..359c740e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,81 +1,78 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "inputs": [ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "inputs": [ + { + "id": "tag", + "description": "Enter the tag to run", + "type": "pickString", + "options": [ + "@smoke", + "@rule1 or @smoke", + "@keyword-asterisk", + "@result:UNDEFINED", + "@result:FAILED", + "@fail_feature" + ] + }, + { + "id": "features", + "description": "Enter the tag to run", + "type": "pickString", + "options": [ + "cucumber_cpp/example/features", + "cucumber_cpp/acceptance_test/features" + ] + } + ], + "configurations": [ + { + "name": "Debug Test", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ { - "id": "tag", - "description": "Enter the tag to run", - "type": "pickString", - "options": [ - "@smoke", - "@rule1 or @smoke", - "@keyword-asterisk", - "@result:UNDEFINED", - "@result:FAILED", - "@fail_feature" - ] - }, - { - "id": "features", - "description": "Enter the tag to run", - "type": "pickString", - "options": [ - "cucumber_cpp/example/features", - "cucumber_cpp/acceptance_test/features" - ] + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true } - ], - "configurations": [ - { - "name": "Debug Test", - "type": "cppdbg", - "request": "launch", - "program": "${command:cmake.launchTargetPath}", - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, + ] + }, + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [ + "run", + "--feature", + "${input:features}", + "--report", + "console", + "junit-xml", + "--tag", + "${input:tag}" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${command:cmake.launchTargetPath}", - "args": [ - "run", - "--feature", - "${input:features}", - "--report", - "console", - "junit-xml", - // "--com", - // "COMx", - // "--nordic", - "--tag", - "${input:tag}" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true } - ] + ] + } + ] } From beb2e2e0580817d003fff43006d4e576606b23f2 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 4 Jul 2025 14:03:23 +0000 Subject: [PATCH 7/7] ad missing stdoutreport ambiguous test --- .../library/report/test/CMakeLists.txt | 1 + .../library/report/test/TestStdOutReport.cpp | 26 ++++++++++++++++--- cucumber_cpp/library/test/TestSteps.cpp | 1 - 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/cucumber_cpp/library/report/test/CMakeLists.txt b/cucumber_cpp/library/report/test/CMakeLists.txt index 63e174fa..55cd6d3f 100644 --- a/cucumber_cpp/library/report/test/CMakeLists.txt +++ b/cucumber_cpp/library/report/test/CMakeLists.txt @@ -7,6 +7,7 @@ target_link_libraries(cucumber_cpp.library.report.test PUBLIC GTest::gmock_main cucumber_cpp.library.report + cucumber_cpp.library.engine.test_helper.steps ) target_sources(cucumber_cpp.library.report.test PRIVATE diff --git a/cucumber_cpp/library/report/test/TestStdOutReport.cpp b/cucumber_cpp/library/report/test/TestStdOutReport.cpp index 8e6721c9..8163e559 100644 --- a/cucumber_cpp/library/report/test/TestStdOutReport.cpp +++ b/cucumber_cpp/library/report/test/TestStdOutReport.cpp @@ -1,5 +1,8 @@ +#include "cucumber_cpp/library/StepRegistry.hpp" #include "cucumber_cpp/library/TraceTime.hpp" +#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/engine/Result.hpp" +#include "cucumber_cpp/library/engine/StepInfo.hpp" #include "cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp" #include "cucumber_cpp/library/report/StdOutReport.hpp" #include "gmock/gmock.h" @@ -137,10 +140,25 @@ namespace cucumber_cpp::library::report TEST_F(TestStdOutReport, StepAmbiguous) { - testing::internal::CaptureStdout(); - // stdOutReport.StepAmbiguous(); - const auto capture{ testing::internal::GetCapturedStdout() }; - EXPECT_THAT(capture, testing::StrEq("")); + cucumber_expression::ParameterRegistry parameterRegistry; + StepRegistry stepRegistry{ parameterRegistry }; + + try + { + (void)stepRegistry.Query("this is ambiguous"); + } + catch (const StepRegistry::AmbiguousStepError& error) + { + cucumber_cpp::library::engine::StepInfo ambiguousStepInfo{ contextManagerInstance.ScenarioContext().info, "ambiguous", {}, {}, {}, {}, error.matches }; + + testing::internal::CaptureStdout(); + stdOutReport.StepAmbiguous("this is ambiguous", ambiguousStepInfo); + const auto capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("Ambiguous step: \"this is ambiguous\" Matches:")); + EXPECT_THAT(capture, testing::HasSubstr("cucumber_cpp/library/engine/test_helper/StepImplementations.cpp:")); + EXPECT_THAT(capture, testing::HasSubstr(":1 : this is ambiguous")); + EXPECT_THAT(capture, testing::HasSubstr(":1 : this is ambiguous( or not)")); + } } TEST_F(TestStdOutReport, StepSkipped) diff --git a/cucumber_cpp/library/test/TestSteps.cpp b/cucumber_cpp/library/test/TestSteps.cpp index 9abac6ba..7bbbf9ee 100644 --- a/cucumber_cpp/library/test/TestSteps.cpp +++ b/cucumber_cpp/library/test/TestSteps.cpp @@ -13,7 +13,6 @@ namespace cucumber_cpp::library { struct TestSteps : testing::Test { - cucumber_expression::ParameterRegistry parameterRegistry; StepRegistry stepRegistry{ parameterRegistry }; };