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
2 changes: 2 additions & 0 deletions compatibility/BaseCompatibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ namespace compatibility
.retry = devkit.kitString.starts_with("retry") ? 2u : 0u,
.strict = true,
.retryTagExpression = cucumber_cpp::library::tag_expression::Parse(""),
.repeat = devkit.kitString.starts_with("repeat") ? 3u : 1u,
.repeatTagExpression = cucumber_cpp::library::tag_expression::Parse(devkit.kitString.starts_with("repeat") ? "@Performance" : ""),
},
};

Expand Down
1 change: 1 addition & 0 deletions compatibility/repeat/repeat.arguments.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--repeat 3 --repeat-tag-filter "@Performance"
18 changes: 18 additions & 0 deletions compatibility/repeat/repeat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "cucumber_cpp/Steps.hpp"

namespace
{
auto executionCount = 0;
auto taggedExecutionCount = 0;
}

GIVEN(R"(a step that counts executions)")
{
executionCount++;
}

GIVEN(R"(a step that counts tagged executions)")
{
taggedExecutionCount++;
}

10 changes: 10 additions & 0 deletions compatibility/repeat/repeat.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Repeat
Some Cucumber implementations support a Repeat mechanism, where scenarios
can be repeated a given number of times for performance measurement.

Scenario: Untagged scenarios run once even when repeat is set
Given a step that counts executions

@Performance
Scenario: Tagged scenarios are repeated when matching the repeat tag filter
Given a step that counts tagged executions
26 changes: 26 additions & 0 deletions compatibility/repeat/repeat.ndjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{"source":{"data":"Feature: Repeat\n Some Cucumber implementations support a Repeat mechanism, where scenarios\n can be repeated a given number of times for performance measurement.\n\n Scenario: Untagged scenarios run once even when repeat is set\n Given a step that counts executions\n\n @Performance\n Scenario: Tagged scenarios are repeated when matching the repeat tag filter\n Given a step that counts tagged executions\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/repeat/repeat.feature"}}
{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"1","keyword":"Scenario","location":{"column":3},"name":"Untagged scenarios run once even when repeat is set","steps":[{"id":"0","keyword":"Given ","keywordType":"Context","location":{"column":5},"text":"a step that counts executions"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"4","keyword":"Scenario","location":{"column":3},"name":"Tagged scenarios are repeated when matching the repeat tag filter","steps":[{"id":"2","keyword":"Given ","keywordType":"Context","location":{"column":5},"text":"a step that counts tagged executions"}],"tags":[{"id":"3","location":{"column":3},"name":"@Performance"}]}}],"description":" Some Cucumber implementations support a Repeat mechanism, where scenarios\n can be repeated a given number of times for performance measurement.","keyword":"Feature","language":"en","location":{"column":1},"name":"Repeat","tags":[]},"uri":"samples/repeat/repeat.feature"}}
{"pickle":{"astNodeIds":["1"],"id":"6","language":"en","location":{"column":3},"name":"Untagged scenarios run once even when repeat is set","steps":[{"astNodeIds":["0"],"id":"5","text":"a step that counts executions","type":"Context"}],"tags":[],"uri":"samples/repeat/repeat.feature"}}
{"pickle":{"astNodeIds":["4"],"id":"8","language":"en","location":{"column":3},"name":"Tagged scenarios are repeated when matching the repeat tag filter","steps":[{"astNodeIds":["2"],"id":"7","text":"a step that counts tagged executions","type":"Context"}],"tags":[{"astNodeId":"3","name":"@Performance"}],"uri":"samples/repeat/repeat.feature"}}
{"stepDefinition":{"id":"9","pattern":{"source":"a step that counts executions","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{},"uri":"samples/repeat/repeat.cpp"}}}
{"stepDefinition":{"id":"10","pattern":{"source":"a step that counts tagged executions","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{},"uri":"samples/repeat/repeat.cpp"}}}
{"testRunStarted":{"id":"11","timestamp":{"nanos":0,"seconds":0}}}
{"testCase":{"id":"12","pickleId":"6","testRunStartedId":"11","testSteps":[{"id":"13","pickleStepId":"5","stepDefinitionIds":["9"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}}
{"testCase":{"id":"14","pickleId":"8","testRunStartedId":"11","testSteps":[{"id":"15","pickleStepId":"7","stepDefinitionIds":["10"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}}
{"testCaseStarted":{"attempt":0,"id":"16","testCaseId":"12","timestamp":{"nanos":1000000,"seconds":0}}}
{"testStepStarted":{"testCaseStartedId":"16","testStepId":"13","timestamp":{"nanos":2000000,"seconds":0}}}
{"testStepFinished":{"testCaseStartedId":"16","testStepId":"13","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}}
{"testCaseFinished":{"testCaseStartedId":"16","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}}
{"testCaseStarted":{"attempt":0,"id":"17","testCaseId":"14","timestamp":{"nanos":5000000,"seconds":0}}}
{"testStepStarted":{"testCaseStartedId":"17","testStepId":"15","timestamp":{"nanos":6000000,"seconds":0}}}
{"testStepFinished":{"testCaseStartedId":"17","testStepId":"15","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}}
{"testCaseFinished":{"testCaseStartedId":"17","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":false}}
{"testCaseStarted":{"attempt":0,"id":"18","testCaseId":"14","timestamp":{"nanos":9000000,"seconds":0}}}
{"testStepStarted":{"testCaseStartedId":"18","testStepId":"15","timestamp":{"nanos":10000000,"seconds":0}}}
{"testStepFinished":{"testCaseStartedId":"18","testStepId":"15","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}}
{"testCaseFinished":{"testCaseStartedId":"18","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}}
{"testCaseStarted":{"attempt":0,"id":"19","testCaseId":"14","timestamp":{"nanos":13000000,"seconds":0}}}
{"testStepStarted":{"testCaseStartedId":"19","testStepId":"15","timestamp":{"nanos":14000000,"seconds":0}}}
{"testStepFinished":{"testCaseStartedId":"19","testStepId":"15","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}}
{"testCaseFinished":{"testCaseStartedId":"19","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}}
{"testRunFinished":{"success":true,"testRunStartedId":"11","timestamp":{"nanos":17000000,"seconds":0}}}
4 changes: 4 additions & 0 deletions cucumber_cpp/library/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ namespace cucumber_cpp::library
cli.add_option("--order", options.ordering, "Run scenarios in specificed order")->transform(CLI::CheckedTransformer(orderingMap, CLI::ignore_case))->default_val(options.ordering);
auto* retryOpt = cli.add_option("--retry", options.retry, "Number of times to retry failed scenarios")->default_val(options.retry);
cli.add_option("--retry-tag-filter", options.retryTagFilter, "Only retry scenarios matching this tag expression")->needs(retryOpt);
auto* repeatOpt = cli.add_option("--repeat", options.repeat, "Number of times to repeat scenarios")->default_val(options.repeat)->check(CLI::PositiveNumber);
cli.add_option("--repeat-tag-filter", options.repeatTagFilter, "Only repeat scenarios matching this tag expression")->needs(repeatOpt);
cli.add_flag("--strict,!--no-strict", options.strict, "Fail if there are pending steps")->default_val(options.strict);
cli.add_flag("--feature-hooks,!--no-feature-hooks", options.featureHooks, "Run Before/After Feature hooks, note these are non-standard and are not supported by messages")->default_val(options.featureHooks);
cli.add_flag("--recursive,!--no-recursive", options.recursive, "Search for feature files recursively")->default_val(options.recursive);
Expand Down Expand Up @@ -197,6 +199,8 @@ namespace cucumber_cpp::library
.retry = options.retry,
.strict = options.strict,
.retryTagExpression = tag_expression::Parse(fmt::to_string(fmt::join(options.retryTagFilter, " "))),
.repeat = options.repeat,
.repeatTagExpression = tag_expression::Parse(fmt::to_string(fmt::join(options.repeatTagFilter, " "))),
.featureHooks = options.featureHooks,
Comment thread
art-pogorelov marked this conversation as resolved.
},
};
Expand Down
3 changes: 3 additions & 0 deletions cucumber_cpp/library/Application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ namespace cucumber_cpp::library
std::size_t retry{ 0 };
std::vector<std::string> retryTagFilter{};

std::size_t repeat{ 1 };
std::vector<std::string> repeatTagFilter{};

bool strict{ true };

bool featureHooks{ false };
Expand Down
45 changes: 32 additions & 13 deletions cucumber_cpp/library/runtime/Worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ namespace cucumber_cpp::library::runtime
return 0;
}

std::size_t RepeatsForPickle(const cucumber::messages::pickle& pickle, const support::RunOptions::Runtime& options)
{
if (options.repeat <= 1)
return 1;
if (options.repeatTagExpression->Evaluate(util::TransformPickleTags(pickle.tags)))
return options.repeat;
return 1;
}

bool IsFailing(cucumber::messages::test_step_result_status status, bool dryRun)
{
if (dryRun)
Expand Down Expand Up @@ -133,21 +142,31 @@ namespace cucumber_cpp::library::runtime

bool Worker::RunTestCase(const cucumber::messages::gherkin_document& gherkinDocument, const assemble::AssembledTestCase& assembledTestCase, Context& testSuiteContext, bool failing)
{
TestCaseRunner testCaseRunner{
broadcaster,
idGenerator,
gherkinDocument,
assembledTestCase.pickle,
assembledTestCase.testCase,
RetriesForPickle(assembledTestCase.pickle, options),
options.dryRun || (options.failFast && failing),
supportCodeLibrary,
testSuiteContext,
};
const auto repeats = RepeatsForPickle(assembledTestCase.pickle, options);
const auto retries = RetriesForPickle(assembledTestCase.pickle, options);
bool allPassed = true;

const auto status = testCaseRunner.Run();
for (std::size_t r = 0; r < repeats; ++r)
{
TestCaseRunner testCaseRunner{
broadcaster,
idGenerator,
gherkinDocument,
assembledTestCase.pickle,
assembledTestCase.testCase,
retries,
options.dryRun || (options.failFast && failing),
supportCodeLibrary,
testSuiteContext,
};

if (const auto status = testCaseRunner.Run(); IsStatusFailed(status))
{
allPassed = false;
Comment thread
art-pogorelov marked this conversation as resolved.
}
}

return !IsStatusFailed(status);
return allPassed;
}

std::vector<cucumber::messages::test_step_result> Worker::RunBeforeTestSuiteHooks(const cucumber::messages::feature& feature, Context& context)
Expand Down
2 changes: 2 additions & 0 deletions cucumber_cpp/library/support/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ namespace cucumber_cpp::library::support
std::size_t retry{ 0 };
bool strict{ true };
std::unique_ptr<tag_expression::Expression> retryTagExpression{};
std::size_t repeat{ 1 };
std::unique_ptr<tag_expression::Expression> repeatTagExpression{};
bool featureHooks{ false };
} runtime;

Expand Down
Loading