diff --git a/.vscode/launch.json b/.vscode/launch.json index b6c90e28..359c740e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,63 +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": "(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 } - ] + ] + } + ] } 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/steps/Steps.cpp b/cucumber_cpp/acceptance_test/steps/Steps.cpp index fc55018d..514b7091 100644 --- a/cucumber_cpp/acceptance_test/steps/Steps.cpp +++ b/cucumber_cpp/acceptance_test/steps/Steps.cpp @@ -50,6 +50,16 @@ GIVEN("a ← tuple\\({float}, {float}, {float}, {float}\\)", (float /* unused */ // empty } +GIVEN("this is ambiguous") +{ + // empty +} + +GIVEN("this is ambiguous( or not)") +{ + // empty +} + THEN("this should be skipped") { FAIL(); diff --git a/cucumber_cpp/acceptance_test/test.bats b/cucumber_cpp/acceptance_test/test.bats index d20b8656..85003cd4 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 this should be skipped" } +@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 336dc5d2..211cb845 100644 --- a/cucumber_cpp/library/StepRegistry.cpp +++ b/cucumber_cpp/library/StepRegistry.cpp @@ -9,6 +9,7 @@ #include "cucumber_cpp/library/engine/Table.hpp" #include #include +#include #include #include #include @@ -21,10 +22,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(const std::string& expression) + StepRegistry::StepMatch StepRegistry::Query(const std::string& expression) { std::vector matches; @@ -33,7 +34,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; } } @@ -64,12 +65,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 ab0d3b4a..d67d202b 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(const std::string& expression); @@ -91,7 +96,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; @@ -107,19 +112,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; @@ -133,9 +140,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 56dfc797..465f2382 100644 --- a/cucumber_cpp/library/engine/FeatureFactory.cpp +++ b/cucumber_cpp/library/engine/FeatureFactory.cpp @@ -283,8 +283,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 @@ -296,11 +297,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..171b5f4f 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::test_helper::ReportForwarderMock reportForwarderImpl{ contextManager }; + FeatureTreeFactory featureTreeFactory{ stepRegistry, reportForwarderImpl }; }; TEST_F(TestFeatureFactory, CreateEmptyFeature) @@ -309,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/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/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/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/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/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 a70b6f15..44a2330a 100644 --- a/cucumber_cpp/library/report/Report.hpp +++ b/cucumber_cpp/library/report/Report.hpp @@ -42,6 +42,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; @@ -88,6 +91,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; @@ -161,6 +167,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..089d40a5 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 @@ -166,8 +169,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) @@ -184,7 +186,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 +203,32 @@ namespace cucumber_cpp::library::report } } + void StdOutReport::StepMissing(const std::string& stepText) + { + std::cout << "\n" + << TcRed + << std::format(R"(Step missing: "{}")", stepText); + std::cout << TcDefault; + } + + void StdOutReport::StepAmbiguous(const std::string& stepText, const engine::StepInfo& stepInfo) + { + std::cout << "\n" + << TcRed + << std::format(R"(Ambiguous step: "{}" Matches:)", 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; diff --git a/cucumber_cpp/library/report/test/CMakeLists.txt b/cucumber_cpp/library/report/test/CMakeLists.txt new file mode 100644 index 00000000..55cd6d3f --- /dev/null +++ b/cucumber_cpp/library/report/test/CMakeLists.txt @@ -0,0 +1,15 @@ +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 + cucumber_cpp.library.engine.test_helper.steps +) + +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..8163e559 --- /dev/null +++ b/cucumber_cpp/library/report/test/TestStdOutReport.cpp @@ -0,0 +1,285 @@ +#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" +#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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, FeatureEndPrintNothing) + { + testing::internal::CaptureStdout(); + stdOutReport.FeatureEnd({}, contextManagerInstance.FeatureContext().info, {}); + 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::StrEq("")); + } + + TEST_F(TestStdOutReport, RuleEndPrintNothing) + { + testing::internal::CaptureStdout(); + stdOutReport.RuleEnd({}, contextManagerInstance.RuleContext().info, {}); + 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 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n\\->")); + EXPECT_THAT(capture, 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, 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 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 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 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 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 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("Step missing: \"missing step text\"")); + } + + TEST_F(TestStdOutReport, StepAmbiguous) + { + 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) + { + testing::internal::CaptureStdout(); + stdOutReport.StepSkipped(contextManagerInstance.StepContext().info); + 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, testing::HasSubstr("\n| \\-> ")); + EXPECT_THAT(capture, 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 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 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 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 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 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 capture{ testing::internal::GetCapturedStdout() }; + EXPECT_THAT(capture, 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 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\"")); + } + +} 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)); }; 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 }; };