Skip to content

Commit 7d9bd7f

Browse files
committed
fix(hooks): don't execute the current scope when an error occurs during a before hook
1 parent b0a312c commit 7d9bd7f

File tree

2 files changed

+62
-26
lines changed

2 files changed

+62
-26
lines changed

cucumber_cpp/library/engine/HookExecutor.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "cucumber_cpp/library/engine/StepInfo.hpp"
88
#include <functional>
99
#include <set>
10+
#include <stdexcept>
1011
#include <string>
1112

1213
namespace cucumber_cpp::library::engine
@@ -18,21 +19,19 @@ namespace cucumber_cpp::library::engine
1819
constexpr HookPair scenarioHooks{ HookType::before, HookType::after };
1920
constexpr HookPair stepHooks{ HookType::beforeStep, HookType::afterStep };
2021

22+
struct HookFailed : std::runtime_error
23+
{
24+
using runtime_error::runtime_error;
25+
};
26+
2127
void ExecuteHook(cucumber_cpp::library::engine::RunnerContext& runnerContext, HookType hook, const std::set<std::string, std::less<>>& tags)
2228
{
23-
try
29+
for (const auto& match : HookRegistry::Instance().Query(hook, tags))
2430
{
25-
for (const auto& match : HookRegistry::Instance().Query(hook, tags))
26-
{
27-
match.factory(runnerContext)->Execute();
31+
match.factory(runnerContext)->Execute();
2832

29-
if (runnerContext.ExecutionStatus() != cucumber_cpp::library::engine::Result::passed)
30-
return;
31-
}
32-
}
33-
catch (...)
34-
{
35-
runnerContext.ExecutionStatus(cucumber_cpp::library::engine::Result::failed);
33+
if (runnerContext.ExecutionStatus() != cucumber_cpp::library::engine::Result::passed)
34+
throw HookFailed{ "hook failed" };
3635
}
3736
}
3837
}
@@ -42,12 +41,31 @@ namespace cucumber_cpp::library::engine
4241
, hookPair{ hookPair }
4342
, tags{ tags }
4443
{
45-
ExecuteHook(runnerContext, hookPair.before, tags);
44+
try
45+
{
46+
ExecuteHook(runnerContext, hookPair.before, tags);
47+
}
48+
catch (HookFailed&)
49+
{
50+
/* This throw ensures no tests are executed in and below the current scope. */
51+
throw HookFailed{ "Error during BEFORE_x_HOOK" };
52+
}
4653
}
4754

4855
HookExecutor::ScopedHook::~ScopedHook()
4956
{
50-
ExecuteHook(runnerContext, hookPair.after, tags);
57+
try
58+
{
59+
ExecuteHook(runnerContext, hookPair.after, tags);
60+
}
61+
catch (HookFailed&)
62+
{
63+
/*
64+
Can't throw from a destructor. Error state will be handled by calling functions.
65+
66+
Errors during AFTER_ALL hook is not a problem, because no other hooks will be executed.
67+
*/
68+
}
5169
}
5270

5371
HookExecutor::ProgramScope::ProgramScope(cucumber_cpp::library::engine::ContextManager& contextManager)

cucumber_cpp/library/engine/TestRunner.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
#include "cucumber_cpp/library/engine/TestRunner.hpp"
3+
#include "cucumber_cpp/library/BodyMacro.hpp"
34
#include "cucumber_cpp/library/engine/FeatureFactory.hpp"
45
#include "cucumber_cpp/library/engine/FeatureInfo.hpp"
56
#include "cucumber_cpp/library/engine/RuleInfo.hpp"
@@ -37,10 +38,15 @@ namespace cucumber_cpp::library::engine
3738

3839
void TestRunnerImpl::Run(const std::vector<std::unique_ptr<FeatureInfo>>& feature)
3940
{
40-
auto scope = testExecution.StartRun();
41+
auto run = [this, &feature]
42+
{
43+
auto scope = testExecution.StartRun();
4144

42-
for (const auto& featurePtr : feature)
43-
RunFeature(*featurePtr);
45+
for (const auto& featurePtr : feature)
46+
RunFeature(*featurePtr);
47+
};
48+
49+
ASSERT_NO_THROW(run());
4450
}
4551

4652
void TestRunnerImpl::NestedStep(StepType type, std::string step)
@@ -51,20 +57,28 @@ namespace cucumber_cpp::library::engine
5157

5258
void TestRunnerImpl::RunFeature(const FeatureInfo& feature)
5359
{
54-
if (feature.Rules().empty() && feature.Scenarios().empty())
55-
return;
60+
auto run = [this, &feature]
61+
{
62+
if (feature.Rules().empty() && feature.Scenarios().empty())
63+
return;
5664

57-
const auto featureScope = testExecution.StartFeature(feature);
65+
const auto featureScope = testExecution.StartFeature(feature);
5866

59-
RunRules(feature.Rules());
60-
RunScenarios(feature.Scenarios());
67+
RunRules(feature.Rules());
68+
RunScenarios(feature.Scenarios());
69+
};
70+
ASSERT_NO_THROW(run());
6171
}
6272

6373
void TestRunnerImpl::RunRule(const RuleInfo& rule)
6474
{
65-
const auto ruleScope = testExecution.StartRule(rule);
75+
auto run = [this, &rule]
76+
{
77+
const auto ruleScope = testExecution.StartRule(rule);
6678

67-
RunScenarios(rule.Scenarios());
79+
RunScenarios(rule.Scenarios());
80+
};
81+
ASSERT_NO_THROW(run());
6882
}
6983

7084
void TestRunnerImpl::RunRules(const std::vector<std::unique_ptr<RuleInfo>>& rules)
@@ -75,11 +89,15 @@ namespace cucumber_cpp::library::engine
7589

7690
void TestRunnerImpl::RunScenario(const ScenarioInfo& scenario)
7791
{
78-
const auto scenarioScope = testExecution.StartScenario(scenario);
92+
auto run = [this, &scenario]
93+
{
94+
const auto scenarioScope = testExecution.StartScenario(scenario);
7995

80-
currentScenario = &scenario;
96+
currentScenario = &scenario;
8197

82-
ExecuteSteps(scenario);
98+
ExecuteSteps(scenario);
99+
};
100+
ASSERT_NO_THROW(run());
83101
}
84102

85103
void TestRunnerImpl::RunScenarios(const std::vector<std::unique_ptr<ScenarioInfo>>& scenarios)

0 commit comments

Comments
 (0)