Skip to content

Commit c4fa230

Browse files
committed
Merge #159 Pass regex captures as step function arguments
This closes #112
2 parents cbb2493 + 0374a5c commit c4fa230

File tree

12 files changed

+241
-51
lines changed

12 files changed

+241
-51
lines changed

HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* Listen on localhost by default to avoid firewall warnings ([#158](https://github.com/cucumber/cucumber-cpp/pull/158) Nik Reiman)
66
* Better integrate Qt into buildsystem, it can now be disabled, and test it in CI ([#160](https://github.com/cucumber/cucumber-cpp/pull/160) Kamil Strzempowicz & Giel van Schijndel)
7+
* Support taking regex captures as arguments to the step definition's function ([#159](https://github.com/cucumber/cucumber-cpp/pull/159) Giel van Schijndel)
78

89
### Bugfixes
910

examples/Calc/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ target_include_directories(Calc PUBLIC src)
66
if(GMOCK_FOUND)
77
add_executable(GTestCalculatorSteps features/step_definitions/GTestCalculatorSteps)
88
target_link_libraries(GTestCalculatorSteps Calc ${CUKE_LIBRARIES} ${CUKE_GTEST_LIBRARIES})
9+
10+
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_variadic_templates HAS_VARIADIC_TEMPLATES)
11+
if(HAS_VARIADIC_TEMPLATES GREATER -1)
12+
add_executable(FuncArgsCalculatorSteps features/step_definitions/FuncArgsCalculatorSteps)
13+
target_link_libraries(FuncArgsCalculatorSteps Calc ${CUKE_LIBRARIES} ${CUKE_GTEST_LIBRARIES})
14+
15+
target_compile_features(FuncArgsCalculatorSteps PRIVATE cxx_variadic_templates)
16+
endif()
917
endif()
1018

1119
if(Boost_UNIT_TEST_FRAMEWORK_FOUND)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <gtest/gtest.h>
2+
#include <cucumber-cpp/autodetect.hpp>
3+
4+
#include <Calculator.h>
5+
6+
using cucumber::ScenarioScope;
7+
8+
struct CalcCtx {
9+
Calculator calc;
10+
double result;
11+
};
12+
13+
GIVEN("^I have entered (\\d+) into the calculator$", (const double n)) {
14+
ScenarioScope<CalcCtx> context;
15+
16+
context->calc.push(n);
17+
}
18+
19+
WHEN("^I press add") {
20+
ScenarioScope<CalcCtx> context;
21+
22+
context->result = context->calc.add();
23+
}
24+
25+
WHEN("^I press divide") {
26+
ScenarioScope<CalcCtx> context;
27+
28+
context->result = context->calc.divide();
29+
}
30+
31+
THEN("^the result should be (.*) on the screen$", (const double expected)) {
32+
ScenarioScope<CalcCtx> context;
33+
34+
EXPECT_EQ(expected, context->result);
35+
}

include/cucumber-cpp/internal/RegistrationMacros.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@
2121
// ************** CUKE OBJECTS ************** //
2222
// ************************************************************************** //
2323

24-
#define CUKE_OBJECT_(class_name, parent_class, registration_fn) \
24+
#define CUKE_OBJECT_(class_name, parent_class, registration_fn, args) \
2525
class class_name : public parent_class { \
2626
public: \
27-
void body(); \
27+
void body() { \
28+
return invokeWithArgs(*this, &class_name::bodyWithArgs); \
29+
} \
30+
void bodyWithArgs args; \
2831
private: \
2932
static const int cukeRegId; \
3033
}; \
3134
const int class_name ::cukeRegId = registration_fn ; \
32-
void class_name ::body() \
35+
void class_name ::bodyWithArgs args \
3336
/**/
3437

3538
#endif /* CUKE_REGISTRATIONMACROS_HPP_ */

include/cucumber-cpp/internal/hook/HookMacros.hpp

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
BEFORE_WITH_NAME_(CUKE_GEN_OBJECT_NAME_, "" #__VA_ARGS__) \
1212
/**/
1313

14-
#define BEFORE_WITH_NAME_(step_name, tag_expression) \
15-
CUKE_OBJECT_( \
16-
step_name, \
17-
::cucumber::internal::BeforeHook, \
18-
BEFORE_HOOK_REGISTRATION_(step_name, tag_expression) \
19-
) \
14+
#define BEFORE_WITH_NAME_(step_name, tag_expression) \
15+
CUKE_OBJECT_( \
16+
step_name, \
17+
::cucumber::internal::BeforeHook, \
18+
BEFORE_HOOK_REGISTRATION_(step_name, tag_expression), \
19+
() \
20+
) \
2021
/**/
2122

2223
#define BEFORE_HOOK_REGISTRATION_(step_name, tag_expression) \
@@ -31,12 +32,13 @@ ::cucumber::internal::registerBeforeHook<step_name>(tag_expression) \
3132
AROUND_STEP_WITH_NAME_(CUKE_GEN_OBJECT_NAME_, "" #__VA_ARGS__) \
3233
/**/
3334

34-
#define AROUND_STEP_WITH_NAME_(step_name, tag_expression) \
35-
CUKE_OBJECT_( \
36-
step_name, \
37-
::cucumber::internal::AroundStepHook, \
38-
AROUND_STEP_HOOK_REGISTRATION_(step_name, tag_expression) \
39-
) \
35+
#define AROUND_STEP_WITH_NAME_(step_name, tag_expression) \
36+
CUKE_OBJECT_( \
37+
step_name, \
38+
::cucumber::internal::AroundStepHook, \
39+
AROUND_STEP_HOOK_REGISTRATION_(step_name, tag_expression), \
40+
() \
41+
) \
4042
/**/
4143

4244
#define AROUND_STEP_HOOK_REGISTRATION_(step_name, tag_expression) \
@@ -51,12 +53,13 @@ ::cucumber::internal::registerAroundStepHook<step_name>(tag_expression) \
5153
AFTER_STEP_WITH_NAME_(CUKE_GEN_OBJECT_NAME_, "" #__VA_ARGS__) \
5254
/**/
5355

54-
#define AFTER_STEP_WITH_NAME_(step_name, tag_expression) \
55-
CUKE_OBJECT_( \
56-
step_name, \
57-
::cucumber::internal::AfterStepHook, \
58-
AFTER_STEP_HOOK_REGISTRATION_(step_name, tag_expression) \
59-
) \
56+
#define AFTER_STEP_WITH_NAME_(step_name, tag_expression) \
57+
CUKE_OBJECT_( \
58+
step_name, \
59+
::cucumber::internal::AfterStepHook, \
60+
AFTER_STEP_HOOK_REGISTRATION_(step_name, tag_expression), \
61+
() \
62+
) \
6063
/**/
6164

6265
#define AFTER_STEP_HOOK_REGISTRATION_(step_name, tag_expression) \
@@ -72,12 +75,13 @@ ::cucumber::internal::registerAfterStepHook<step_name>(tag_expression) \
7275
AFTER_WITH_NAME_(CUKE_GEN_OBJECT_NAME_, "" #__VA_ARGS__) \
7376
/**/
7477

75-
#define AFTER_WITH_NAME_(step_name, tag_expression) \
76-
CUKE_OBJECT_( \
77-
step_name, \
78-
::cucumber::internal::AfterHook, \
79-
AFTER_HOOK_REGISTRATION_(step_name, tag_expression) \
80-
) \
78+
#define AFTER_WITH_NAME_(step_name, tag_expression) \
79+
CUKE_OBJECT_( \
80+
step_name, \
81+
::cucumber::internal::AfterHook, \
82+
AFTER_HOOK_REGISTRATION_(step_name, tag_expression), \
83+
() \
84+
) \
8185
/**/
8286

8387
#define AFTER_HOOK_REGISTRATION_(step_name, tag_expression) \
@@ -96,7 +100,8 @@ BEFORE_ALL_WITH_NAME_(CUKE_GEN_OBJECT_NAME_) \
96100
CUKE_OBJECT_( \
97101
step_name, \
98102
::cucumber::internal::BeforeAllHook, \
99-
BEFORE_ALL_HOOK_REGISTRATION_(step_name) \
103+
BEFORE_ALL_HOOK_REGISTRATION_(step_name), \
104+
() \
100105
) \
101106
/**/
102107

@@ -116,7 +121,8 @@ AFTER_ALL_WITH_NAME_(CUKE_GEN_OBJECT_NAME_) \
116121
CUKE_OBJECT_( \
117122
step_name, \
118123
::cucumber::internal::AfterAllHook, \
119-
AFTER_ALL_HOOK_REGISTRATION_(step_name) \
124+
AFTER_ALL_HOOK_REGISTRATION_(step_name), \
125+
() \
120126
) \
121127
/**/
122128

include/cucumber-cpp/internal/hook/HookRegistrar.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class Hook {
2727
virtual void body() = 0;
2828
protected:
2929
bool tagsMatch(Scenario *scenario);
30+
31+
template <typename Derived, typename R>
32+
static R invokeWithArgs(Derived& that, R (Derived::* f)()) {
33+
return (that.*f)();
34+
}
3035
private:
3136
shared_ptr<TagExpression> tagExpression;
3237
};

include/cucumber-cpp/internal/step/StepMacros.hpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,24 @@
77
// ************** STEP ************** //
88
// ************************************************************************** //
99

10-
#define CUKE_STEP_(step_matcher) \
11-
CUKE_STEP_WITH_NAME_(CUKE_GEN_OBJECT_NAME_, step_matcher) \
10+
#define CUKE_STEP_GET_MATCHER_(step_matcher, ...) step_matcher
11+
#define CUKE_STEP_GET_ARGS_(step_matcher, args, ...) args
12+
13+
#define CUKE_STEP_(...) \
14+
CUKE_STEP_WITH_NAME_( \
15+
CUKE_GEN_OBJECT_NAME_, \
16+
CUKE_STEP_GET_MATCHER_(__VA_ARGS__), \
17+
CUKE_STEP_GET_ARGS_(__VA_ARGS__, ()) \
18+
) \
1219
/**/
1320

14-
#define CUKE_STEP_WITH_NAME_(step_name, step_matcher) \
15-
CUKE_OBJECT_( \
16-
step_name, \
17-
STEP_INHERITANCE(step_name), \
18-
CUKE_STEP_REGISTRATION_(step_name, step_matcher) \
19-
) \
21+
#define CUKE_STEP_WITH_NAME_(step_name, step_matcher, args) \
22+
CUKE_OBJECT_( \
23+
step_name, \
24+
STEP_INHERITANCE(step_name), \
25+
CUKE_STEP_REGISTRATION_(step_name, step_matcher), \
26+
args \
27+
) \
2028
/**/
2129

2230
#define CUKE_STEP_REGISTRATION_(step_name, step_matcher) \

include/cucumber-cpp/internal/step/StepManager.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@
77
#include <string>
88
#include <vector>
99

10+
#include <boost/config.hpp>
1011
#include <boost/enable_shared_from_this.hpp>
1112
#include <boost/make_shared.hpp>
1213
#include <boost/shared_ptr.hpp>
1314
using boost::shared_ptr;
1415

16+
#ifndef BOOST_NO_VARIADIC_TEMPLATES
17+
#include <type_traits>
18+
#endif
19+
1520
#include "../Table.hpp"
21+
#include "../utils/IndexSequence.hpp"
1622
#include "../utils/Regex.hpp"
1723

1824
namespace cucumber {
@@ -125,6 +131,31 @@ class BasicStep {
125131

126132
template<class T> const T getInvokeArg();
127133
const InvokeArgs *getArgs();
134+
135+
#ifdef BOOST_NO_VARIADIC_TEMPLATES
136+
// Special case for zero arguments, only thing we bother to support on C++98
137+
template <typename Derived, typename R>
138+
static R invokeWithArgs(Derived& that, R (Derived::* f)()) {
139+
return (that.*f)();
140+
}
141+
#else
142+
template <typename Derived, typename R, typename... Args, std::size_t... N>
143+
static R invokeWithIndexedArgs(Derived& that, R (Derived::* f)(Args...), index_sequence<N...>) {
144+
return (that.*f)(
145+
that.pArgs->template getInvokeArg<typename std::decay<Args>::type>(N)...
146+
);
147+
}
148+
149+
template <typename Derived, typename R, typename... Args>
150+
static R invokeWithArgs(Derived& that, R (Derived::* f)(Args...)) {
151+
that.currentArgIndex = sizeof...(Args);
152+
return invokeWithIndexedArgs(
153+
that,
154+
f,
155+
index_sequence_for<Args...>{});
156+
}
157+
#endif
158+
128159
private:
129160
// FIXME: awful hack because of Boost::Test
130161
InvokeResult currentResult;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#ifndef CUKE_INDEXSEQ_HPP_
2+
#define CUKE_INDEXSEQ_HPP_
3+
4+
#include <boost/config.hpp>
5+
6+
#ifndef BOOST_NO_VARIADIC_TEMPLATES
7+
8+
#if __cplusplus < 201402L && !(_MSC_VER > 1800)
9+
#include <cstddef> // for std::size_t
10+
#else
11+
#include <utility>
12+
#endif
13+
14+
namespace cucumber {
15+
namespace internal {
16+
17+
#if __cplusplus < 201402L && !(_MSC_VER > 1800)
18+
// Based on https://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
19+
20+
template <std::size_t... N>
21+
struct index_sequence {
22+
typedef index_sequence type;
23+
};
24+
25+
template <typename Seq1, typename Seq2>
26+
struct concat_index_sequence;
27+
28+
template <std::size_t... N1, std::size_t... N2>
29+
struct concat_index_sequence<
30+
index_sequence<N1...>
31+
, index_sequence<N2...>
32+
> : public index_sequence<
33+
N1...
34+
, (sizeof...(N1) + N2)...
35+
> {
36+
};
37+
38+
template <std::size_t N>
39+
struct make_index_sequence : public concat_index_sequence<
40+
typename make_index_sequence< N/2>::type
41+
, typename make_index_sequence<N-N/2>::type
42+
> {
43+
};
44+
45+
template <> struct make_index_sequence<0> : public index_sequence< > {};
46+
template <> struct make_index_sequence<1> : public index_sequence<0> {};
47+
48+
template <typename... Ts>
49+
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
50+
#else
51+
using ::std::index_sequence;
52+
using ::std::index_sequence_for;
53+
#endif
54+
55+
}
56+
}
57+
58+
#endif /* !BOOST_NO_VARIADIC_TEMPLATES */
59+
60+
#endif /* CUKE_INDEXSEQ_HPP_ */

tests/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ if(GMOCK_FOUND)
3737
cuke_add_test(unit/TagTest)
3838

3939
cuke_add_driver_test(integration/drivers/GTestDriverTest ${CUKE_GTEST_LIBRARIES})
40+
41+
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_variadic_templates HAS_VARIADIC_TEMPLATES)
42+
if(HAS_VARIADIC_TEMPLATES GREATER -1)
43+
target_compile_features(CukeCommandsTest PRIVATE cxx_variadic_templates)
44+
endif()
4045
endif()
4146

4247
if(Boost_UNIT_TEST_FRAMEWORK_FOUND)

0 commit comments

Comments
 (0)