Skip to content

Commit 2110c77

Browse files
committed
test: test cucumber expressions against official test input files
1 parent 7287b4c commit 2110c77

File tree

120 files changed

+1611
-570
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+1611
-570
lines changed

cucumber_cpp/library/cucumber_expression/ParameterRegistry.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ namespace cucumber_cpp::library::cucumber_expression
3232
{
3333
return [](MatchRange matches)
3434
{
35-
std::string str{ StringTo<std::string>(matches.begin()->str()) };
36-
str.erase(0, 1);
37-
str.erase(str.size() - 1, 1);
35+
std::string str = matches[1].matched ? matches[1].str() : matches[3].str();
36+
str = std::regex_replace(str, std::regex(R"__(\\")__"), "\"");
37+
str = std::regex_replace(str, std::regex(R"__(\\')__"), "'");
3838
return str;
3939
};
4040
}
@@ -65,8 +65,8 @@ namespace cucumber_cpp::library::cucumber_expression
6565
const static std::string integerNegativeRegex{ R"__(-?\d+)__" };
6666
const static std::string integerPositiveRegex{ R"__(\d+)__" };
6767
const static std::string floatRegex{ R"__((?=.*\d.*)[-+]?\d*(?:\.(?=\d.*))?\d*(?:\d+[E][+-]?\d+)?)__" };
68-
const static std::string stringDoubleRegex{ R"__("(?:[^"\\]*(?:\.[^"\\]*)*)")__" };
69-
const static std::string stringSingleRegex{ R"__('(?:[^'\\]*(?:\.[^'\\]*)*)')__" };
68+
const static std::string stringDoubleRegex{ R"__("([^\"\\]*(\\.[^\"\\]*)*)")__" };
69+
const static std::string stringSingleRegex{ R"__('([^'\\]*(\\.[^'\\]*)*)')__" };
7070
const static std::string wordRegex{ R"__([^\s]+)__" };
7171

7272
AddParameter("int", { integerNegativeRegex, integerPositiveRegex }, CreateStreamConverter<std::int64_t>());
@@ -89,7 +89,7 @@ namespace cucumber_cpp::library::cucumber_expression
8989
{
9090
if (parameters.contains(name))
9191
return parameters.at(name);
92-
92+
;
9393
return {};
9494
}
9595

cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
#include <any>
66
#include <cctype>
77
#include <cstddef>
8+
#include <cstdint>
89
#include <cstdlib>
910
#include <functional>
11+
#include <iostream>
12+
#include <limits>
1013
#include <map>
1114
#include <regex>
1215
#include <sstream>
@@ -41,6 +44,12 @@ namespace cucumber_cpp::library::cucumber_expression
4144
return std::move(s);
4245
}
4346

47+
template<>
48+
inline int64_t StringTo<std::int64_t>(const std::string& s)
49+
{
50+
return std::stoll(s);
51+
}
52+
4453
template<>
4554
inline float StringTo<float>(const std::string& s)
4655
{

cucumber_cpp/library/cucumber_expression/test/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@ add_test(NAME cucumber_cpp.library.cucumber_expression.test COMMAND cucumber_cpp
44
target_link_libraries(cucumber_cpp.library.cucumber_expression.test PUBLIC
55
gmock_main
66
cucumber_cpp.library.cucumber_expression
7+
yaml-cpp::yaml-cpp
78
)
89

910
target_sources(cucumber_cpp.library.cucumber_expression.test PRIVATE
1011
TestExpression.cpp
1112
TestExpressionParser.cpp
1213
TestExpressionTokenizer.cpp
1314
)
15+
16+
add_custom_command(
17+
TARGET cucumber_cpp.library.cucumber_expression.test POST_BUILD
18+
COMMAND ${CMAKE_COMMAND} -E copy_directory
19+
${CMAKE_SOURCE_DIR}/testdata
20+
$<TARGET_FILE_DIR:cucumber_cpp.library.cucumber_expression.test>/testdata
21+
)

cucumber_cpp/library/cucumber_expression/test/TestExpression.cpp

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11

22
#include "cucumber_cpp/library/cucumber_expression/Expression.hpp"
33
#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp"
4+
#include "yaml-cpp/node/node.h"
5+
#include "yaml-cpp/node/parse.h"
6+
#include "yaml-cpp/yaml.h"
47
#include "gmock/gmock.h"
58
#include <any>
69
#include <cctype>
710
#include <cstdint>
811
#include <cstdlib>
12+
#include <filesystem>
913
#include <format>
1014
#include <functional>
1115
#include <gtest/gtest.h>
@@ -20,6 +24,28 @@
2024

2125
namespace cucumber_cpp::library::cucumber_expression
2226
{
27+
namespace
28+
{
29+
std::vector<std::pair<std::string, YAML::Node>> GetTestData(const std::string& path)
30+
{
31+
std::vector<std::pair<std::string, YAML::Node>> testdata;
32+
33+
for (const auto& file : std::filesystem::directory_iterator(path))
34+
if (file.is_regular_file() && file.path().extension() == ".yaml")
35+
testdata.emplace_back(file.path().string(), YAML::LoadFile(file.path()));
36+
37+
return testdata;
38+
}
39+
40+
std::string FormatMessage(const YAML::Node& node, const Expression& expression)
41+
{
42+
return std::format("failed to match {}\n"
43+
"regex {}\n"
44+
"against {}",
45+
node["expression"].as<std::string>(), expression.Pattern(), node["text"].as<std::string>());
46+
}
47+
}
48+
2349
struct TestExpression : testing::Test
2450
{
2551
ParameterRegistry parameterRegistry{};
@@ -31,41 +57,44 @@ namespace cucumber_cpp::library::cucumber_expression
3157
}
3258
};
3359

34-
TEST_F(TestExpression, Escape)
35-
{
36-
auto match = Match(R"(hello \{world?)", "hello {world?");
37-
EXPECT_THAT(match, testing::IsTrue());
38-
}
39-
40-
TEST_F(TestExpression, MatchOptional)
41-
{
42-
Expression expression{ R"(hello (world))", parameterRegistry };
43-
EXPECT_THAT(expression.Pattern(), testing::StrEq(R"__(^hello (?:world)?$)__"));
44-
EXPECT_THAT(expression.Match("hello world"), testing::IsTrue());
45-
EXPECT_THAT(expression.Match("hello "), testing::IsTrue());
46-
}
47-
48-
TEST_F(TestExpression, MatchAlternative)
60+
TEST_F(TestExpression, TestFromFiles)
4961
{
50-
Expression expression{ R"(hello country/wo\/rld/city)", parameterRegistry };
51-
EXPECT_THAT(expression.Pattern(), testing::StrEq(R"__(^hello (?:country|wo/rld|city)$)__"));
52-
EXPECT_THAT(expression.Match("hello country"), testing::IsTrue());
53-
EXPECT_THAT(expression.Match("hello wo/rld"), testing::IsTrue());
54-
EXPECT_THAT(expression.Match("hello city"), testing::IsTrue());
55-
}
62+
std::filesystem::path testdataPath = "testdata/cucumber-expression/matching";
5663

57-
TEST_F(TestExpression, MatchString)
58-
{
59-
auto match = Match(R"__(hello {string})__", R"__(hello "beautiful world")__");
60-
EXPECT_THAT(match, testing::IsTrue());
61-
EXPECT_THAT(std::any_cast<std::string>((*match)[0]), testing::StrEq(R"__(beautiful world)__"));
62-
}
63-
64-
TEST_F(TestExpression, MatchInt)
65-
{
66-
auto match = Match(R"__(there are {int} cucumbers)__", R"__(there are 15 cucumbers)__");
67-
EXPECT_THAT(match, testing::IsTrue());
68-
EXPECT_THAT(std::any_cast<std::int64_t>((*match)[0]), testing::Eq(15));
64+
for (const auto& [file, testdata] : GetTestData(testdataPath))
65+
{
66+
if (testdata["exception"])
67+
ASSERT_ANY_THROW(Match(testdata["expression"].as<std::string>(), testdata["text"].as<std::string>()))
68+
<< std::format("Test failed for file: {}", file);
69+
else
70+
{
71+
if (testdata["expected_args"].IsNull())
72+
ASSERT_THAT(Match(testdata["expression"].as<std::string>(), testdata["text"].as<std::string>()), testing::IsFalse());
73+
else
74+
{
75+
const auto expression = Expression{ testdata["expression"].as<std::string>(), parameterRegistry };
76+
const auto matchOpt = expression.Match(testdata["text"].as<std::string>());
77+
78+
ASSERT_THAT(matchOpt, testing::IsTrue()) << FormatMessage(testdata, expression);
79+
80+
const auto match = *matchOpt;
81+
for (std::size_t i = 0; i < testdata["expected_args"].size(); ++i)
82+
{
83+
if (match[i].type() == typeid(std::string))
84+
ASSERT_THAT(std::any_cast<std::string>(match[i]), testdata["expected_args"][i].as<std::string>()) << FormatMessage(testdata, expression);
85+
else if (match[i].type() == typeid(std::int64_t))
86+
ASSERT_THAT(std::any_cast<std::int64_t>(match[i]), testdata["expected_args"][i].as<std::int64_t>()) << FormatMessage(testdata, expression);
87+
else if (match[i].type() == typeid(float))
88+
ASSERT_THAT(std::any_cast<float>(match[i]), testdata["expected_args"][i].as<float>()) << FormatMessage(testdata, expression);
89+
else if (match[i].type() == typeid(double))
90+
ASSERT_THAT(std::any_cast<double>(match[i]), testdata["expected_args"][i].as<double>()) << FormatMessage(testdata, expression);
91+
else
92+
FAIL() << "Unknown type: " << match[i].type().name() << " for:\n"
93+
<< FormatMessage(testdata, expression);
94+
}
95+
}
96+
}
97+
}
6998
}
7099

71100
TEST_F(TestExpression, MatchFloat)
@@ -157,10 +186,4 @@ namespace cucumber_cpp::library::cucumber_expression
157186
Expression expression{ expr, parameterRegistry };
158187
EXPECT_THAT(expr, testing::StrEq(expression.Source()));
159188
}
160-
161-
TEST_F(TestExpression, MultiParamParsing)
162-
{
163-
auto matchString{ Match(R"__(Step with cucumber expression syntax {float} {string} {int})__", R"__(Step with cucumber expression syntax 1.1 "string" 10)__") };
164-
EXPECT_THAT(matchString, testing::IsTrue());
165-
}
166189
}

0 commit comments

Comments
 (0)