Commit fbc49a34 authored by Paolo Ambrosio's avatar Paolo Ambrosio Committed by Kamil Strzempowicz
Browse files

Fixed code, separating regex and c-string escaping

Introduced unit tests
Fixed feature file, with bizarre escaping behaviour
parent fb410264
......@@ -13,17 +13,28 @@ Scenario Outline: <characters> in step definition
When Cucumber executes the scenario
Then a step definition snippet with "^the step contains '<step definition string>'$" is suggested
# Remember that in Gherkin's data tables:
# \n -> newline
# \| -> |
# \\ -> \
# \<other> -> <other>
#
# Unfortunately the behaviour is just odd when chaining backslashes:
# \\\\\\. -> \\. (most common case)
# \\\\\\\\+ -> \\+
# \\\\\\\\\\\\\\ -> \\\\
Examples:
| characters | scenario step string | step definition string |
| Double quotes | " | \\" |
| Backslash | \ | \\\\\\ |
| Dot | . | \\. |
| Caret | ^ | \\^ |
| Dollar | $ | \\$ |
| Asterisk | * | \\* |
| Plus | + | \\\+ |
| Question mark | ? | \\\? |
| Brackets | () | \\(\\) |
| Square brackets | [] | \\[\\] |
| Curly brackets | {} | \\{\\} |
| Pipe | \| | \\\| |
| Backslash | \\ | \\\\\\\\\\\\\\ |
| Dot | . | \\\\\\. |
| Caret | ^ | \\\\\\^ |
| Dollar | $ | \\\\\\$ |
| Asterisk | * | \\\\\\* |
| Plus | + | \\\\\\\\+ |
| Question mark | ? | \\\\\\? |
| Brackets | ( ) | \\\\\\( \\\\\\) |
| Square brackets | [ ] | \\\\\\[ \\\\\\] |
| Curly brackets | { } | \\\\\\{ \\\\\\} |
| Pipe | \| | \\\\\\\| |
......@@ -151,26 +151,6 @@ EOF
assert_success true
end
def unescape(string)
string.gsub('\n', "\n").gsub('\e', "\e")
end
def assert_include(expected, actual)
expect(unescape(actual)).to include(unescape(expected))
end
def assert_scenario_output(scenario_output)
assert_include("#{scenario_output}", all_output)
end
def assert_not_include(unexpected, actual)
expect(unescape(actual)).not_to include(unescape(unexpected))
end
def assert_scenario_output_not_include(scenario_output)
assert_not_include("#{scenario_output}", all_output)
end
def assert_scenario_reported_as_failing(scenario_name)
assert_partial_output("# Scenario: #{scenario_name}", all_output)
assert_success false
......
......@@ -6,6 +6,6 @@ Then /^the step output should contain:$/ do |output|
expect(@steps_out.gets).to eq(output)
end
Then /^a step definition snippet with "(.*)" is suggested$/ do |output|
assert_scenario_output output
Then /^a step definition snippet with (".*") is suggested$/ do |regex_string|
assert_partial_output("(#{regex_string}) {", all_output)
end
......@@ -25,13 +25,17 @@ class CukeCommands {
public:
CukeCommands();
virtual ~CukeCommands();
void beginScenario(const TagExpression::tag_list *tags);
void endScenario();
const std::string snippetText(const std::string stepKeyword, const std::string stepName) const;
MatchResult stepMatches(const std::string description) const;
InvokeResult invoke(step_id_type id, const InvokeArgs * pArgs);
protected:
const std::string escapeRegex(const std::string regex) const;
const std::string escapeCString(const std::string str) const;
private:
StepManager stepManager;
HookRegistrar hookRegistrar;
......
......@@ -34,18 +34,24 @@ void CukeCommands::endScenario() {
}
const std::string CukeCommands::snippetText(const std::string stepKeyword, const std::string stepName) const {
const boost::regex esc("[\".^$|()\\[\\]{}*+?\\\\]");
const std::string rep("\\\\&");
std::stringstream text;
text << boost::to_upper_copy(stepKeyword)
<< "(\"^"
<< regex_replace(stepName, esc, rep, boost::match_default | boost::format_sed)
<< "$\") {\n"
<< "(\""
<< escapeCString("^" + escapeRegex(stepName) + "$")
<< "\") {\n"
<< " pending();\n"
<< "}\n";
return text.str();
}
const std::string CukeCommands::escapeRegex(const std::string reg) const {
return regex_replace(reg, boost::regex("[\\|\\(\\)\\[\\]\\{\\}\\^\\$\\*\\+\\?\\.\\\\]"), "\\\\&", boost::match_default | boost::format_sed);
}
const std::string CukeCommands::escapeCString(const std::string str) const {
return regex_replace(str, boost::regex("[\"\\\\]"), "\\\\&", boost::match_default | boost::format_sed);
}
MatchResult CukeCommands::stepMatches(const std::string description) const {
return stepManager.stepMatches(description);
}
......
......@@ -83,7 +83,7 @@ public:
TEST_F(CukeCommandsTest, matchesCorrectly) {
addStepWithMatcher(STATIC_MATCHER);
MatchResult result = cukeCommands.stepMatches(STATIC_MATCHER);
MatchResult result = stepMatches(STATIC_MATCHER);
EXPECT_EQ(stepInfoPtr->id, result.getResultSet().at(0).stepInfo->id);
}
......@@ -96,3 +96,20 @@ TEST_F(CukeCommandsTest, invokeHandlesParametersWithMacro) {
// The real test is in CheckAllParameters::body()
runStepBodyTest<CheckAllParametersWithMacro>();
}
TEST_F(CukeCommandsTest, producesSnippetsEscapingTitle) {
EXPECT_EQ("THEN(\"^x\\\\|y\\\"z$\") {\n"
" pending();\n"
"}\n",
snippetText("then","x|y\"z"));
}
TEST_F(CukeCommandsTest, escapesCaractersInRegexes) {
// abc|()[]{}^$*+?.\def <= abc\|\(\)\[\]\{\}\^\$\*\+\?\.\\def
EXPECT_EQ("abc\\|\\(\\)\\[\\]\\{\\}\\^\\$\\*\\+\\?\\.\\\\def", escapeRegex("abc|()[]{}^$*+?.\\def"));
}
TEST_F(CukeCommandsTest, escapesCharactersInCStrings) {
// abc\"def\\ghi <= abc"def\ghi
EXPECT_EQ("abc\\\"def\\\\ghi", escapeCString("abc\"def\\ghi"));
}
......@@ -14,13 +14,12 @@ class EmptyStep : public GenericStep {
void body() {}
};
class CukeCommandsFixture : public ::testing::Test {
class CukeCommandsFixture : public ::testing::Test, public CukeCommands {
StepManagerTestDouble stepManager;
public:
const static std::string STATIC_MATCHER;
protected:
CukeCommands cukeCommands;
shared_ptr<StepInfo> stepInfoPtr;
template<class T>
......@@ -28,7 +27,7 @@ protected:
addStepToManager<T>(STATIC_MATCHER);
const InvokeArgs *pArgs = T::buildInvokeArgs();
shared_ptr<const InvokeArgs> spArgs(pArgs);
cukeCommands.invoke(stepInfoPtr->id, pArgs);
invoke(stepInfoPtr->id, pArgs);
}
template<class T>
......
......@@ -99,20 +99,20 @@ protected:
}
void beginScenario(const TagExpression::tag_list *tags) {
cukeCommands.beginScenario(tags);
CukeCommandsFixture::beginScenario(tags);
}
void beginScenario(const TagExpression::tag_list & tags) {
TagExpression::tag_list *pTags = new TagExpression::tag_list(tags.begin(), tags.end());
beginScenario(pTags);
CukeCommandsFixture::beginScenario(pTags);
}
void invokeStep() {
cukeCommands.invoke(stepInfoPtr->id, &NO_INVOKE_ARGS);
invoke(stepInfoPtr->id, &NO_INVOKE_ARGS);
}
void endScenario() {
cukeCommands.endScenario();
CukeCommandsFixture::endScenario();
}
std::string sort(std::string str) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment