Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 72 additions & 57 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -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
}
]
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@result:AMBIGUOUS
Feature: Test ambiguous steps
Scenario: Contains ambiguous steps
Given this is ambiguous
When a when step
10 changes: 10 additions & 0 deletions cucumber_cpp/acceptance_test/steps/Steps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
9 changes: 9 additions & 0 deletions cucumber_cpp/acceptance_test/test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion cucumber_cpp/library/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand Down
13 changes: 7 additions & 6 deletions cucumber_cpp/library/StepRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "cucumber_cpp/library/engine/Table.hpp"
#include <cstddef>
#include <memory>
#include <source_location>
#include <span>
#include <string>
#include <utility>
Expand All @@ -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<StepMatch> matches;

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -64,12 +65,12 @@ namespace cucumber_cpp::library
return list;
}

void StepRegistry::Register(const std::string& matcher, engine::StepType stepType, std::unique_ptr<Body> (&factory)(Context& context, const engine::Table& table))
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)
{
if (matcher.starts_with('^') || matcher.ends_with('$'))
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::RegularExpression>, matcher }, factory);
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::RegularExpression>, matcher }, factory, loc);
else
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::Expression>, matcher, parameterRegistry }, factory);
registry.emplace_back(stepType, cucumber_expression::Matcher{ std::in_place_type<cucumber_expression::Expression>, matcher, parameterRegistry }, factory, loc);
}

StepStringRegistration& StepStringRegistration::Instance()
Expand Down
85 changes: 46 additions & 39 deletions cucumber_cpp/library/StepRegistry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <cstdint>
#include <exception>
#include <memory>
#include <source_location>
#include <span>
#include <string>
#include <string_view>
Expand All @@ -27,50 +28,40 @@ namespace cucumber_cpp::library
return std::make_unique<T>(context, table);
}

struct StepMatch
{
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)
: factory(factory)
, matches(std::move(matches))
, stepRegexStr(stepRegexStr)
{}

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{};
};

struct StepRegistry
{
struct StepNotFoundError : std::exception
{
using std::exception::exception;
};

struct AmbiguousStepError : std::exception
{
explicit AmbiguousStepError(std::vector<StepMatch>&& matches)
: matches{ std::move(matches) }
{}

std::vector<StepMatch> matches;
};

struct Entry
{
Entry(engine::StepType type, cucumber_expression::Matcher regex, std::unique_ptr<Body> (&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<Body> (&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<Body> (&factory)(Context& context, const engine::Table& table);
std::source_location loc;

std::uint32_t used{ 0 };
};

struct StepMatch
{
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)
: entry{ entry }
, factory{ factory }
, matches{ std::move(matches) }
, stepRegexStr{ stepRegexStr }
{}

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{};
};

struct EntryView
{
EntryView(const cucumber_expression::Matcher& stepRegex, const std::uint32_t& used)
Expand All @@ -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<StepMatch>&& matches)
: matches{ std::move(matches) }
{}

std::vector<StepMatch> matches;
};

explicit StepRegistry(cucumber_expression::ParameterRegistry& parameterRegistry);

[[nodiscard]] StepMatch Query(const std::string& expression);
Expand All @@ -91,7 +96,7 @@ namespace cucumber_cpp::library
[[nodiscard]] std::vector<EntryView> List() const;

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

std::vector<Entry> registry;
cucumber_expression::ParameterRegistry& parameterRegistry;
Expand All @@ -107,19 +112,21 @@ namespace cucumber_cpp::library

struct Entry
{
Entry(engine::StepType type, std::string regex, std::unique_ptr<Body> (&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<Body> (&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<Body> (&factory)(Context& context, const engine::Table& table);
std::source_location loc;
};

template<class T>
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<Entry> GetEntries();
[[nodiscard]] std::span<const Entry> GetEntries() const;
Expand All @@ -133,9 +140,9 @@ namespace cucumber_cpp::library
//////////////////////////

template<class T>
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<T>);
Instance().registry.emplace_back(stepType, matcher, StepBodyFactory<T>, loc);

return Instance().registry.size();
}
Expand Down
Loading
Loading