Skip to content

Commit de2aaeb

Browse files
committed
add hooks-named, hooks-skipped and hooks-undefined compatibility tests
1 parent dfe7157 commit de2aaeb

File tree

15 files changed

+229
-158
lines changed

15 files changed

+229
-158
lines changed

compatibility/compatibility.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ namespace compatibility
329329

330330
TEST(CompatibilityTest, KIT_NAME)
331331
{
332+
if (std::string{ KIT_FOLDER }.ends_with("markdown"))
333+
GTEST_SKIP();
334+
332335
compatibility::StopwatchIncremental stopwatch;
333336
compatibility::TimestampGeneratorIncremental timestampGenerator;
334337
compatibility::RunDevkit(compatibility::LoadDevkit());
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "cucumber_cpp/CucumberCpp.hpp"
2+
#include <gmock/gmock.h>
3+
4+
HOOK_BEFORE_SCENARIO(.name = "A named before hook")
5+
{
6+
// no-op
7+
}
8+
9+
WHEN(R"(a step passes)")
10+
{
11+
// no-op
12+
}
13+
14+
HOOK_AFTER_SCENARIO(.name = "A named after hook")
15+
{
16+
// no-op
17+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "cucumber_cpp/CucumberCpp.hpp"
2+
#include <gmock/gmock.h>
3+
4+
HOOK_BEFORE_SCENARIO()
5+
{
6+
// no-op
7+
}
8+
9+
HOOK_BEFORE_SCENARIO("@skip-before")
10+
{
11+
Skipped();
12+
}
13+
14+
HOOK_BEFORE_SCENARIO()
15+
{
16+
// no-op
17+
}
18+
19+
WHEN(R"(a normal step)")
20+
{
21+
// no-op
22+
}
23+
24+
WHEN(R"(a step that skips)")
25+
{
26+
Skipped();
27+
}
28+
29+
HOOK_AFTER_SCENARIO()
30+
{
31+
// no-op
32+
}
33+
34+
HOOK_AFTER_SCENARIO("@skip-after")
35+
{
36+
Skipped();
37+
}
38+
39+
HOOK_AFTER_SCENARIO()
40+
{
41+
// no-op
42+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "cucumber_cpp/CucumberCpp.hpp"
2+
#include <gmock/gmock.h>
3+
4+
HOOK_BEFORE_SCENARIO()
5+
{
6+
// no-op
7+
}
8+
9+
HOOK_AFTER_SCENARIO()
10+
{
11+
// no-op
12+
}

cucumber_cpp/library/Body.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "cucumber/messages/exception.hpp"
33
#include "cucumber/messages/test_step_result.hpp"
44
#include "cucumber/messages/test_step_result_status.hpp"
5+
#include "cucumber_cpp/library/engine/ExecutionContext.hpp"
56
#include "cucumber_cpp/library/support/Duration.hpp"
67
#include "gtest/gtest.h"
78
#include <chrono>
@@ -57,6 +58,18 @@ namespace cucumber_cpp::library
5758
support::Stopwatch::Instance().Start();
5859
Execute(args);
5960
}
61+
catch (const engine::StepSkipped& e)
62+
{
63+
testStepResult.status = cucumber::messages::test_step_result_status::SKIPPED;
64+
if (!e.message.empty())
65+
testStepResult.message = e.message;
66+
}
67+
catch (const engine::StepPending& e)
68+
{
69+
testStepResult.status = cucumber::messages::test_step_result_status::PENDING;
70+
if (!e.message.empty())
71+
testStepResult.message = e.message;
72+
}
6073
catch (const FatalError& error)
6174
{
6275
testStepResult.status = cucumber::messages::test_step_result_status::FAILED;

cucumber_cpp/library/HookRegistry.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,20 @@ namespace cucumber_cpp::library
6767
: engine::ExecutionContext{ broadCaster, context, stepOrHookStarted }
6868
{}
6969

70-
HookRegistry::Definition::Definition(std::string id, HookType type, std::string_view expression, HookFactory factory, std::source_location sourceLocation)
70+
HookRegistry::Definition::Definition(std::string id, HookType type, std::optional<std::string_view> expression, std::optional<std::string_view> name, HookFactory factory, std::source_location sourceLocation)
7171
: type{ type }
72-
, tagExpression{ tag_expression::Parse(expression) }
72+
, tagExpression{ tag_expression::Parse(expression.value_or("")) }
7373
, factory{ factory }
7474
, hook{
7575
.id = id,
76+
.name = name.has_value() ? std::make_optional<std::string>(name.value()) : std::nullopt,
7677
.source_reference = cucumber::messages::source_reference{
7778
.uri = sourceLocation.file_name(),
7879
.location = cucumber::messages::location{
7980
.line = sourceLocation.line(),
8081
},
8182
},
82-
.tag_expression = !expression.empty() ? std::make_optional(std::string{ expression }) : std::nullopt,
83+
.tag_expression = expression.has_value() ? std::make_optional<std::string>(expression.value()) : std::nullopt,
8384
.type = HookTypeMap.contains(type) ? std::make_optional(HookTypeMap.at(type)) : std::nullopt,
8485
}
8586
{}
@@ -92,7 +93,7 @@ namespace cucumber_cpp::library
9293
void HookRegistry::LoadHooks()
9394
{
9495
for (const auto& matcher : support::DefinitionRegistration::Instance().GetHooks())
95-
Register(matcher.id, matcher.type, matcher.expression, matcher.factory, matcher.sourceLocation);
96+
Register(matcher.id, matcher.type, matcher.expression, matcher.name, matcher.factory, matcher.sourceLocation);
9697
}
9798

9899
std::vector<std::string> HookRegistry::FindIds(HookType hookType, std::span<const cucumber::messages::pickle_tag> tags) const
@@ -127,18 +128,18 @@ namespace cucumber_cpp::library
127128
return registry.at(id);
128129
}
129130

130-
void HookRegistry::Register(std::string id, HookType type, std::string_view expression, HookFactory factory, std::source_location sourceLocation)
131+
void HookRegistry::Register(std::string id, HookType type, std::optional<std::string_view> expression, std::optional<std::string_view> name, HookFactory factory, std::source_location sourceLocation)
131132
{
132-
registry.emplace(id, Definition{ id, type, expression, factory, sourceLocation });
133+
registry.emplace(id, Definition{ id, type, expression, name, factory, sourceLocation });
133134
}
134135

135-
std::span<HookRegistration::Entry> HookRegistration::GetEntries()
136-
{
137-
return registry;
138-
}
136+
// std::span<HookEntry> HookRegistration::GetEntries()
137+
// {
138+
// return registry;
139+
// }
139140

140-
std::span<const HookRegistration::Entry> HookRegistration::GetEntries() const
141-
{
142-
return registry;
143-
}
141+
// std::span<const HookEntry> HookRegistration::GetEntries() const
142+
// {
143+
// return registry;
144+
// }
144145
}

cucumber_cpp/library/HookRegistry.hpp

Lines changed: 3 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <cstdint>
1515
#include <map>
1616
#include <memory>
17+
#include <optional>
1718
#include <ranges>
1819
#include <source_location>
1920
#include <span>
@@ -23,7 +24,6 @@
2324

2425
namespace cucumber_cpp::library
2526
{
26-
2727
enum struct HookType
2828
{
2929
beforeAll,
@@ -74,7 +74,7 @@ namespace cucumber_cpp::library
7474
{
7575
struct Definition
7676
{
77-
Definition(std::string id, HookType type, std::string_view expression, HookFactory factory, std::source_location sourceLocation);
77+
Definition(std::string id, HookType type, std::optional<std::string_view> expression, std::optional<std::string_view> name, HookFactory factory, std::source_location sourceLocation);
7878

7979
HookType type;
8080
std::unique_ptr<tag_expression::Expression> tagExpression;
@@ -112,91 +112,11 @@ namespace cucumber_cpp::library
112112
const Definition& GetDefinitionById(std::string id) const;
113113

114114
private:
115-
void Register(std::string id, HookType type, std::string_view expression, HookFactory factory, std::source_location sourceLocation);
115+
void Register(std::string id, HookType type, std::optional<std::string_view> expression, std::optional<std::string_view> name, HookFactory factory, std::source_location sourceLocation);
116116

117117
cucumber::gherkin::id_generator_ptr idGenerator;
118118
std::map<std::string, Definition> registry;
119119
};
120-
121-
struct GlobalHook
122-
{
123-
std::string_view name{ "anonymous" };
124-
std::int32_t order{ 0 };
125-
};
126-
127-
struct Hook
128-
{
129-
std::string_view tagExpression{ "" };
130-
std::string_view name{ "anonymous" };
131-
std::int32_t order{ 0 };
132-
};
133-
134-
struct HookRegistration
135-
{
136-
private:
137-
HookRegistration() = default;
138-
139-
public:
140-
static inline HookRegistration& Instance()
141-
{
142-
static HookRegistration instance;
143-
return instance;
144-
}
145-
146-
struct Entry
147-
{
148-
Entry(HookType type, std::string_view expression, HookFactory factory, std::source_location sourceLocation)
149-
: type(type)
150-
, expression{ expression }
151-
, factory(factory)
152-
, sourceLocation{ sourceLocation }
153-
{}
154-
155-
HookType type;
156-
std::string_view expression;
157-
HookFactory factory;
158-
std::source_location sourceLocation;
159-
std::string id{ "unassigned" };
160-
};
161-
162-
template<class T>
163-
static std::size_t Register(std::string_view tagExpression, HookType hookType, std::source_location sourceLocation = std::source_location::current());
164-
template<class T>
165-
static std::size_t Register(Hook hook, HookType hookType, std::source_location sourceLocation = std::source_location::current());
166-
template<class T>
167-
static std::size_t Register(GlobalHook hook, HookType hookType, std::source_location sourceLocation = std::source_location::current());
168-
169-
std::span<Entry> GetEntries();
170-
[[nodiscard]] std::span<const Entry> GetEntries() const;
171-
172-
private:
173-
std::vector<Entry> registry;
174-
};
175-
176-
//////////////////////////
177-
// implementation //
178-
//////////////////////////
179-
180-
template<class T>
181-
std::size_t HookRegistration::Register(std::string_view tagExpression, HookType hookType, std::source_location sourceLocation)
182-
{
183-
Instance().registry.emplace_back(hookType, tagExpression, HookBodyFactory<T>, sourceLocation);
184-
return Instance().registry.size();
185-
}
186-
187-
template<class T>
188-
std::size_t HookRegistration::Register(Hook hook, HookType hookType, std::source_location sourceLocation)
189-
{
190-
Instance().registry.emplace_back(hookType, hook.tagExpression, HookBodyFactory<T>, sourceLocation);
191-
return Instance().registry.size();
192-
}
193-
194-
template<class T>
195-
std::size_t HookRegistration::Register(GlobalHook hook, HookType hookType, std::source_location sourceLocation)
196-
{
197-
Instance().registry.emplace_back(hookType, "", HookBodyFactory<T>, sourceLocation);
198-
return Instance().registry.size();
199-
}
200120
}
201121

202122
#endif

cucumber_cpp/library/Hooks.hpp

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,44 @@
1111
// #define HOOK_(matcher, type) BODY(matcher, type, (), cucumber_cpp::library::HookRegistration::Register, cucumber_cpp::library::HookBase)
1212
#define HOOK_(matcher, type) BODY(matcher, type, (), cucumber_cpp::library::support::DefinitionRegistration::Register, cucumber_cpp::library::HookBase)
1313

14-
#define HOOK_BEFORE_ALL(...) \
15-
HOOK_( \
16-
(cucumber_cpp::library::GlobalHook{ __VA_ARGS__ }), \
14+
#define HOOK_BEFORE_ALL(...) \
15+
HOOK_( \
16+
(cucumber_cpp::library::support::GlobalHook{ __VA_ARGS__ }), \
1717
cucumber_cpp::library::HookType::beforeAll)
1818

19-
#define HOOK_AFTER_ALL(...) \
20-
HOOK_( \
21-
(cucumber_cpp::library::GlobalHook{ __VA_ARGS__ }), \
19+
#define HOOK_AFTER_ALL(...) \
20+
HOOK_( \
21+
(cucumber_cpp::library::support::GlobalHook{ __VA_ARGS__ }), \
2222
cucumber_cpp::library::HookType::afterAll)
2323

24-
#define HOOK_BEFORE_FEATURE(...) \
25-
HOOK_( \
26-
(cucumber_cpp::library::Hook{ __VA_ARGS__ }), \
24+
#define HOOK_BEFORE_FEATURE(...) \
25+
HOOK_( \
26+
(cucumber_cpp::library::support::Hook{ __VA_ARGS__ }), \
2727
cucumber_cpp::library::HookType::beforeFeature)
2828

29-
#define HOOK_AFTER_FEATURE(...) \
30-
HOOK_( \
31-
(cucumber_cpp::library::Hook{ __VA_ARGS__ }), \
29+
#define HOOK_AFTER_FEATURE(...) \
30+
HOOK_( \
31+
(cucumber_cpp::library::support::Hook{ __VA_ARGS__ }), \
3232
cucumber_cpp::library::HookType::afterFeature)
3333

34-
#define HOOK_BEFORE_SCENARIO(...) \
35-
HOOK_( \
36-
(cucumber_cpp::library::Hook{ __VA_ARGS__ }), \
34+
#define HOOK_BEFORE_SCENARIO(...) \
35+
HOOK_( \
36+
(cucumber_cpp::library::support::Hook{ __VA_ARGS__ }), \
3737
cucumber_cpp::library::HookType::before)
3838

39-
#define HOOK_AFTER_SCENARIO(...) \
40-
HOOK_( \
41-
(cucumber_cpp::library::Hook{ __VA_ARGS__ }), \
39+
#define HOOK_AFTER_SCENARIO(...) \
40+
HOOK_( \
41+
(cucumber_cpp::library::support::Hook{ __VA_ARGS__ }), \
4242
cucumber_cpp::library::HookType::after)
4343

44-
#define HOOK_BEFORE_STEP(...) \
45-
HOOK_( \
46-
(cucumber_cpp::library::Hook{ __VA_ARGS__ }), \
44+
#define HOOK_BEFORE_STEP(...) \
45+
HOOK_( \
46+
(cucumber_cpp::library::support::Hook{ __VA_ARGS__ }), \
4747
cucumber_cpp::library::HookType::beforeStep)
4848

49-
#define HOOK_AFTER_STEP(...) \
50-
HOOK_( \
51-
(cucumber_cpp::library::Hook{ __VA_ARGS__ }), \
49+
#define HOOK_AFTER_STEP(...) \
50+
HOOK_( \
51+
(cucumber_cpp::library::support::Hook{ __VA_ARGS__ }), \
5252
cucumber_cpp::library::HookType::afterStep)
5353

5454
#endif

cucumber_cpp/library/assemble/AssembleTestSuites.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ namespace cucumber_cpp::library::assemble
7171
stepMatchArgumentsLists);
7272
}
7373

74-
for (const auto& hookId : supportCodeLibrary.hookRegistry.FindIds(HookType::after, pickleSource.pickle->tags))
74+
for (const auto& hookId : supportCodeLibrary.hookRegistry.FindIds(HookType::after, pickleSource.pickle->tags) | std::views::reverse)
7575
testCase.test_steps.emplace_back(hookId, idGenerator->next_id());
7676

7777
broadcaster.BroadcastEvent(cucumber::messages::envelope{ .test_case = testCase });

cucumber_cpp/library/engine/ExecutionContext.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <istream>
1111
#include <iterator>
1212
#include <optional>
13+
#include <source_location>
1314
#include <string>
1415
#include <utility>
1516
#include <variant>
@@ -98,6 +99,16 @@ namespace cucumber_cpp::library::engine
9899
});
99100
}
100101

102+
void ExecutionContext::Skipped(const std::string& message, std::source_location current) noexcept(false)
103+
{
104+
throw StepSkipped{ message, current };
105+
}
106+
107+
void ExecutionContext::Pending(const std::string& message, std::source_location current) noexcept(false)
108+
{
109+
throw StepPending{ message, current };
110+
}
111+
101112
void ExecutionContext::Attach(std::string data, cucumber::messages::attachment_content_encoding encoding, OptionsOrMediaType mediaType)
102113
{
103114
const auto options = std::holds_alternative<std::string>(mediaType)

0 commit comments

Comments
 (0)