Skip to content

Commit fe6e9ab

Browse files
committed
feat: added early console print statements on missing or ambiguous steps
1 parent 552df48 commit fe6e9ab

21 files changed

+192
-79
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@result:AMBIGUOUS
2+
Feature: Test ambiguous steps
3+
Scenario: Contains ambiguous steps
4+
Given this is ambiguous
5+
When a when step

cucumber_cpp/acceptance_test/features/test_scenarios.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ Feature: Simple feature file
2020
Scenario: A scenario with undefined step
2121
Given a given step
2222
When a when step
23-
Then a when step
23+
Then a missing step
2424
Then a then step

cucumber_cpp/acceptance_test/steps/Steps.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,13 @@ GIVEN("a ← tuple\\({float}, {float}, {float}, {float}\\)", (float /* unused */
5050
{
5151
// empty
5252
}
53+
54+
GIVEN("this is ambiguous")
55+
{
56+
// empty
57+
}
58+
59+
GIVEN("this is ambiguous( or not)")
60+
{
61+
// empty
62+
}

cucumber_cpp/acceptance_test/test.bats

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,19 @@ teardown() {
126126
@test "Dry run with known missing steps" {
127127
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
128128
assert_failure
129+
assert_output --partial "Step missing: \"a missing step\""
129130
assert_output --partial "undefined \"cucumber_cpp/acceptance_test/features/test_scenarios.feature\""
130131
assert_output --partial "skipped Then a then step"
131132
}
132133

134+
@test "Dry run with known ambiguous steps" {
135+
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
136+
assert_failure
137+
assert_output --partial "Ambiguous step: \"this is ambiguous\" Matches"
138+
assert_output --partial "ambiguous \"cucumber_cpp/acceptance_test/features/test_ambiguous_steps.feature\""
139+
assert_output --partial "skipped When a when step"
140+
}
141+
133142
@test "Test the and keyword" {
134143
run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test run --feature cucumber_cpp/acceptance_test/features --tag "@keyword-and" --report console
135144
assert_success

cucumber_cpp/library/Application.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ namespace cucumber_cpp::library
201201
engine::TestExecutionImpl testExecution{ contextManager, reporters, hookExecution, runPolicy };
202202

203203
StepRegistry stepRegistry{ parameterRegistry };
204-
engine::FeatureTreeFactory featureTreeFactory{ stepRegistry };
204+
engine::FeatureTreeFactory featureTreeFactory{ stepRegistry, reporters };
205205

206206
engine::TestRunnerImpl testRunner{ featureTreeFactory, testExecution };
207207

cucumber_cpp/library/StepRegistry.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <cstddef>
1212
#include <memory>
1313
#include <ranges>
14+
#include <source_location>
1415
#include <span>
1516
#include <string>
1617
#include <utility>
@@ -34,10 +35,10 @@ namespace cucumber_cpp::library
3435
: parameterRegistry{ parameterRegistry }
3536
{
3637
for (const auto& matcher : StepStringRegistration::Instance().GetEntries())
37-
Register(matcher.regex, matcher.type, matcher.factory);
38+
Register(matcher.regex, matcher.type, matcher.factory, matcher.loc);
3839
}
3940

40-
StepMatch StepRegistry::Query(engine::StepType stepType, const std::string& expression)
41+
StepRegistry::StepMatch StepRegistry::Query(engine::StepType stepType, const std::string& expression)
4142
{
4243
std::vector<StepMatch> matches;
4344

@@ -46,7 +47,7 @@ namespace cucumber_cpp::library
4647
auto match = std::visit(cucumber_expression::MatchVisitor{ expression }, entry.regex);
4748
if (match)
4849
{
49-
matches.emplace_back(entry.factory, *match, std::visit(cucumber_expression::PatternVisitor{}, entry.regex));
50+
matches.emplace_back(entry, entry.factory, *match, std::visit(cucumber_expression::PatternVisitor{}, entry.regex));
5051
++entry.used;
5152
}
5253
}
@@ -82,12 +83,12 @@ namespace cucumber_cpp::library
8283
return list;
8384
}
8485

85-
void StepRegistry::Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table))
86+
void StepRegistry::Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table), std::source_location loc)
8687
{
8788
if (matcher.starts_with('^') || matcher.ends_with('$'))
88-
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::RegularExpression>, matcher }, factory);
89+
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::RegularExpression>, matcher }, factory, loc);
8990
else
90-
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::Expression>, matcher, parameterRegistry }, factory);
91+
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::Expression>, matcher, parameterRegistry }, factory, loc);
9192
}
9293

9394
StepStringRegistration& StepStringRegistration::Instance()

cucumber_cpp/library/StepRegistry.hpp

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <cstdint>
1313
#include <exception>
1414
#include <memory>
15+
#include <source_location>
1516
#include <span>
1617
#include <string>
1718
#include <string_view>
@@ -27,50 +28,40 @@ namespace cucumber_cpp::library
2728
return std::make_unique<T>(context, table);
2829
}
2930

30-
struct StepMatch
31-
{
32-
StepMatch(std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table), std::variant<std::vector<std::string>, std::vector<std::any>> matches, std::string_view stepRegexStr)
33-
: factory(factory)
34-
, matches(std::move(matches))
35-
, stepRegexStr(stepRegexStr)
36-
{}
37-
38-
std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table);
39-
std::variant<std::vector<std::string>, std::vector<std::any>> matches{};
40-
std::string_view stepRegexStr{};
41-
};
42-
4331
struct StepRegistry
4432
{
45-
struct StepNotFoundError : std::exception
46-
{
47-
using std::exception::exception;
48-
};
49-
50-
struct AmbiguousStepError : std::exception
51-
{
52-
explicit AmbiguousStepError(std::vector<StepMatch>&& matches)
53-
: matches{ std::move(matches) }
54-
{}
55-
56-
std::vector<StepMatch> matches;
57-
};
58-
5933
struct Entry
6034
{
61-
Entry(engine::StepType type, cucumber_expression::Matcher regex, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table))
62-
: type(type)
63-
, regex(std::move(regex))
64-
, factory(factory)
35+
Entry(engine::StepType type, cucumber_expression::Matcher regex, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table), std::source_location loc)
36+
: type{ type }
37+
, regex{ std::move(regex) }
38+
, factory{ factory }
39+
, loc{ loc }
6540
{}
6641

6742
engine::StepType type{};
6843
cucumber_expression::Matcher regex;
6944
std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table);
45+
std::source_location loc;
7046

7147
std::uint32_t used{ 0 };
7248
};
7349

50+
struct StepMatch
51+
{
52+
StepMatch(Entry& entry, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table), std::variant<std::vector<std::string>, std::vector<std::any>> matches, std::string_view stepRegexStr)
53+
: entry{ entry }
54+
, factory{ factory }
55+
, matches{ std::move(matches) }
56+
, stepRegexStr{ stepRegexStr }
57+
{}
58+
59+
Entry& entry;
60+
std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table);
61+
std::variant<std::vector<std::string>, std::vector<std::any>> matches{};
62+
std::string_view stepRegexStr{};
63+
};
64+
7465
struct EntryView
7566
{
7667
EntryView(const cucumber_expression::Matcher& stepRegex, const std::uint32_t& used)
@@ -82,6 +73,20 @@ namespace cucumber_cpp::library
8273
const std::uint32_t& used;
8374
};
8475

76+
struct StepNotFoundError : std::exception
77+
{
78+
using std::exception::exception;
79+
};
80+
81+
struct AmbiguousStepError : std::exception
82+
{
83+
explicit AmbiguousStepError(std::vector<StepMatch>&& matches)
84+
: matches{ std::move(matches) }
85+
{}
86+
87+
std::vector<StepMatch> matches;
88+
};
89+
8590
explicit StepRegistry(cucumber_expression::ParameterRegistry& parameterRegistry);
8691

8792
[[nodiscard]] StepMatch Query(engine::StepType stepType, const std::string& expression);
@@ -92,7 +97,7 @@ namespace cucumber_cpp::library
9297
[[nodiscard]] std::vector<EntryView> List() const;
9398

9499
private:
95-
void Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table));
100+
void Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table), std::source_location loc);
96101

97102
std::vector<Entry> registry;
98103
cucumber_expression::ParameterRegistry& parameterRegistry;
@@ -108,19 +113,21 @@ namespace cucumber_cpp::library
108113

109114
struct Entry
110115
{
111-
Entry(engine::StepType type, std::string regex, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table))
112-
: type(type)
113-
, regex(std::move(regex))
114-
, factory(factory)
116+
Entry(engine::StepType type, std::string regex, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table), std::source_location loc)
117+
: type{ type }
118+
, regex{ std::move(regex) }
119+
, factory{ factory }
120+
, loc{ loc }
115121
{}
116122

117123
engine::StepType type{};
118124
std::string regex;
119125
std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table);
126+
std::source_location loc;
120127
};
121128

122129
template<class T>
123-
static std::size_t Register(const std::string& matcher, engine::StepType stepType);
130+
static std::size_t Register(const std::string& matcher, engine::StepType stepType, std::source_location loc = std::source_location::current());
124131

125132
std::span<Entry> GetEntries();
126133
[[nodiscard]] std::span<const Entry> GetEntries() const;
@@ -134,9 +141,9 @@ namespace cucumber_cpp::library
134141
//////////////////////////
135142

136143
template<class T>
137-
std::size_t StepStringRegistration::Register(const std::string& matcher, engine::StepType stepType)
144+
std::size_t StepStringRegistration::Register(const std::string& matcher, engine::StepType stepType, std::source_location loc)
138145
{
139-
Instance().registry.emplace_back(stepType, matcher, StepBodyFactory<T>);
146+
Instance().registry.emplace_back(stepType, matcher, StepBodyFactory<T>, loc);
140147

141148
return Instance().registry.size();
142149
}

cucumber_cpp/library/engine/FeatureFactory.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,9 @@ namespace cucumber_cpp::library::engine
284284
}
285285
}
286286

287-
FeatureTreeFactory::FeatureTreeFactory(StepRegistry& stepRegistry)
287+
FeatureTreeFactory::FeatureTreeFactory(StepRegistry& stepRegistry, report::ReportForwarder& reportHandler)
288288
: stepRegistry{ stepRegistry }
289+
, reportHandler{ reportHandler }
289290
{}
290291

291292
std::unique_ptr<StepInfo> FeatureTreeFactory::CreateStepInfo(StepType stepType, std::string stepText, const ScenarioInfo& scenarioInfo, std::size_t line, std::size_t column, std::vector<std::vector<TableValue>> table) const
@@ -297,11 +298,15 @@ namespace cucumber_cpp::library::engine
297298
}
298299
catch (const StepRegistry::StepNotFoundError&)
299300
{
300-
return std::make_unique<StepInfo>(scenarioInfo, std::move(stepText), stepType, line, column, std::move(table));
301+
auto stepInfo = std::make_unique<StepInfo>(scenarioInfo, stepText, stepType, line, column, std::move(table));
302+
reportHandler.StepMissing(stepText);
303+
return stepInfo;
301304
}
302305
catch (StepRegistry::AmbiguousStepError& ase)
303306
{
304-
return std::make_unique<StepInfo>(scenarioInfo, std::move(stepText), stepType, line, column, std::move(table), std::move(ase.matches));
307+
auto stepInfo = std::make_unique<StepInfo>(scenarioInfo, stepText, stepType, line, column, std::move(table), std::move(ase.matches));
308+
reportHandler.StepAmbiguous(stepText, *stepInfo);
309+
return stepInfo;
305310
}
306311
}
307312

cucumber_cpp/library/engine/FeatureFactory.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "cucumber_cpp/library/engine/StepInfo.hpp"
88
#include "cucumber_cpp/library/engine/StepType.hpp"
99
#include "cucumber_cpp/library/engine/Table.hpp"
10+
#include "cucumber_cpp/library/report/Report.hpp"
1011
#include <cstddef>
1112
#include <filesystem>
1213
#include <memory>
@@ -18,14 +19,15 @@ namespace cucumber_cpp::library::engine
1819
{
1920
struct FeatureTreeFactory
2021
{
21-
explicit FeatureTreeFactory(StepRegistry& stepRegistry);
22+
FeatureTreeFactory(StepRegistry& stepRegistry, report::ReportForwarder& reportHandler);
2223

2324
[[nodiscard]] std::unique_ptr<StepInfo> CreateStepInfo(StepType stepType, std::string stepText, const ScenarioInfo& scenarioInfo, std::size_t line, std::size_t column, std::vector<std::vector<TableValue>> table) const;
2425

2526
[[nodiscard]] std::unique_ptr<FeatureInfo> Create(const std::filesystem::path& path, std::string_view tagExpression) const;
2627

2728
private:
2829
StepRegistry& stepRegistry;
30+
report::ReportForwarder& reportHandler;
2931
};
3032
}
3133

cucumber_cpp/library/engine/StepInfo.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace cucumber_cpp::library::engine
2020
{
2121
}
2222

23-
StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector<std::vector<TableValue>> table, struct StepMatch stepMatch)
23+
StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector<std::vector<TableValue>> table, StepRegistry::StepMatch stepMatch)
2424
: scenarioInfo{ scenarioInfo }
2525
, text{ std::move(text) }
2626
, type{ type }
@@ -31,7 +31,7 @@ namespace cucumber_cpp::library::engine
3131
{
3232
}
3333

34-
StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector<std::vector<TableValue>> table, std::vector<struct StepMatch> stepMatches)
34+
StepInfo::StepInfo(const struct ScenarioInfo& scenarioInfo, std::string text, StepType type, std::size_t line, std::size_t column, std::vector<std::vector<TableValue>> table, std::vector<StepRegistry::StepMatch> stepMatches)
3535
: scenarioInfo{ scenarioInfo }
3636
, text{ std::move(text) }
3737
, type{ type }
@@ -72,7 +72,7 @@ namespace cucumber_cpp::library::engine
7272
return table;
7373
}
7474

75-
const std::variant<std::monostate, struct StepMatch, std::vector<struct StepMatch>>& StepInfo::StepMatch() const
75+
const std::variant<std::monostate, StepRegistry::StepMatch, std::vector<StepRegistry::StepMatch>>& StepInfo::StepMatch() const
7676
{
7777
return stepMatch;
7878
}

0 commit comments

Comments
 (0)