Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cucumber_cpp/acceptance_test/MainCustom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ int main(int argc, char** argv)
cucumber_cpp::Application application{};

application.CliParser().add_flag("--required", *application.ProgramContext().EmplaceAt<bool>("--required"))->required();
application.CliParser().add_flag("--failprogramhook", *application.ProgramContext().EmplaceAt<bool>("--failprogramhook"));

return application.Run(argc, argv);
}
9 changes: 9 additions & 0 deletions cucumber_cpp/acceptance_test/hooks/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
HOOK_BEFORE_ALL()
{
std::cout << "HOOK_BEFORE_ALL\n";

if (context.Contains("--failprogramhook") && context.Get<bool>("--failprogramhook"))
ASSERT_THAT(false, testing::IsTrue());
}

HOOK_AFTER_ALL()
Expand Down Expand Up @@ -47,3 +50,9 @@ HOOK_BEFORE_SCENARIO("@throw_scenariohook")
{
throw std::string{ "error" };
}

HOOK_BEFORE_SCENARIO()
{
if (context.Contains("--failprogramhook") && context.Get<bool>("--failprogramhook"))
std::cout << "should not be executed\n";
}
9 changes: 9 additions & 0 deletions cucumber_cpp/acceptance_test/test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,12 @@ teardown() {
assert_output --partial "skipped Given a given step"
assert_output --partial "tests : 0/1 passed"
}


@test "Test error program hook results in error and skipped steps" {
run .build/Host/cucumber_cpp/acceptance_test/Debug/cucumber_cpp.acceptance_test.custom run --feature cucumber_cpp/acceptance_test/features --tag "@smoke and @result:OK" --report console --required --failprogramhook
assert_failure
assert_output --partial "skipped Given a given step"
refute_output --partial "should not be executed"
# assert_output --partial "tests : 0/1 passed"
}
2 changes: 1 addition & 1 deletion cucumber_cpp/library/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ namespace cucumber_cpp::library

int Application::GetExitCode() const
{
if (contextManager.ProgramContext().ExecutionStatus() == engine::Result::passed)
if (contextManager.ProgramContext().EffectiveExecutionStatus() == engine::Result::passed)
return 0;
else
return 1;
Expand Down
41 changes: 37 additions & 4 deletions cucumber_cpp/library/engine/ContextManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "cucumber_cpp/library/engine/RuleInfo.hpp"
#include "cucumber_cpp/library/engine/ScenarioInfo.hpp"
#include "cucumber_cpp/library/engine/StepInfo.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -48,11 +49,29 @@ namespace cucumber_cpp::library::engine
Start();
}

[[nodiscard]] Result CurrentContext::InheritedExecutionStatus() const
{
if (parent == nullptr)
return executionStatus;
else
return std::max(executionStatus, parent->InheritedExecutionStatus());
}

[[nodiscard]] Result CurrentContext::EffectiveExecutionStatus() const
{
return std::max(executionStatus, nestedExecutionStatus);
}

[[nodiscard]] Result CurrentContext::ExecutionStatus() const
{
return executionStatus;
}

[[nodiscard]] Result CurrentContext::NestedExecutionStatus() const
{
return nestedExecutionStatus;
}

void CurrentContext::Start()
{
traceTime.Start();
Expand All @@ -69,7 +88,16 @@ namespace cucumber_cpp::library::engine
executionStatus = result;

if (parent != nullptr)
parent->ExecutionStatus(result);
parent->NestedExecutionStatus(result);
}

void CurrentContext::NestedExecutionStatus(Result result)
{
if (result > nestedExecutionStatus)
nestedExecutionStatus = result;

if (parent != nullptr)
parent->NestedExecutionStatus(result);
}

ProgramContext::ProgramContext(std::shared_ptr<ContextStorageFactory> contextStorageFactory)
Expand Down Expand Up @@ -189,14 +217,11 @@ namespace cucumber_cpp::library::engine
ContextManager::ScopedStepContext ContextManager::CreateStepContext(const StepInfo& stepInfo)
{
stepContext.push(std::make_shared<cucumber_cpp::library::engine::StepContext>(*scenarioContext, stepInfo));
runnerContext.push(stepContext.top());

return ScopedStepContext{ *this };
}

void ContextManager::DisposeStepContext()
{
runnerContext.pop();
stepContext.pop();
}

Expand All @@ -212,4 +237,12 @@ namespace cucumber_cpp::library::engine
{
return *runnerContext.top();
}

cucumber_cpp::library::engine::RunnerContext* ContextManager::CurrentStepContext()
{
if (stepContext.empty())
return nullptr;

return stepContext.top().get();
}
}
7 changes: 7 additions & 0 deletions cucumber_cpp/library/engine/ContextManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ namespace cucumber_cpp::library::engine
explicit CurrentContext(std::shared_ptr<ContextStorageFactory> contextStorageFactory);
explicit CurrentContext(CurrentContext* parent);

[[nodiscard]] Result InheritedExecutionStatus() const;
[[nodiscard]] Result EffectiveExecutionStatus() const;
[[nodiscard]] Result ExecutionStatus() const;
[[nodiscard]] Result NestedExecutionStatus() const;

void Start();
[[nodiscard]] TraceTime::Duration Duration() const;
Expand All @@ -28,9 +31,12 @@ namespace cucumber_cpp::library::engine
void ExecutionStatus(Result result);

private:
void NestedExecutionStatus(Result result);

CurrentContext* parent{ nullptr };

Result executionStatus{ Result::passed };
Result nestedExecutionStatus{ Result::passed };
TraceTime traceTime;
};

Expand Down Expand Up @@ -93,6 +99,7 @@ namespace cucumber_cpp::library::engine
cucumber_cpp::library::engine::StepContext& StepContext();

cucumber_cpp::library::engine::RunnerContext& CurrentContext();
cucumber_cpp::library::engine::RunnerContext* CurrentStepContext();

private:
void DisposeFeatureContext();
Expand Down
3 changes: 3 additions & 0 deletions cucumber_cpp/library/engine/FailureHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ namespace cucumber_cpp::library::engine

contextManager.CurrentContext().ExecutionStatus(cucumber_cpp::library::engine::Result::failed);

if (auto* stepContext = contextManager.CurrentStepContext(); stepContext != nullptr)
stepContext->ExecutionStatus(cucumber_cpp::library::engine::Result::failed);

reportHandler.Failure(message, relativeFilePath, line);
}

Expand Down
10 changes: 3 additions & 7 deletions cucumber_cpp/library/engine/HookExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include "cucumber_cpp/library/engine/StepInfo.hpp"
#include <functional>
#include <set>
#include <stdexcept>
#include <string>
#include <utility>

namespace cucumber_cpp::library::engine
{
Expand All @@ -20,20 +22,14 @@ namespace cucumber_cpp::library::engine

void ExecuteHook(cucumber_cpp::library::engine::RunnerContext& runnerContext, HookType hook, const std::set<std::string, std::less<>>& tags)
{
try
{
if (runnerContext.InheritedExecutionStatus() == Result::passed)
for (const auto& match : HookRegistry::Instance().Query(hook, tags))
{
match.factory(runnerContext)->Execute();

if (runnerContext.ExecutionStatus() != cucumber_cpp::library::engine::Result::passed)
return;
}
}
catch (...)
{
runnerContext.ExecutionStatus(cucumber_cpp::library::engine::Result::failed);
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion cucumber_cpp/library/engine/TestExecution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ namespace cucumber_cpp::library::engine
void TestExecutionImpl::RunStep(const cucumber_cpp::library::engine::StepInfo& stepInfo)
{
auto scopedContext = contextManager.CreateStepContext(stepInfo);
if (contextManager.ScenarioContext().ExecutionStatus() == cucumber_cpp::library::engine::Result::passed)
if (contextManager.ScenarioContext().InheritedExecutionStatus() == Result::passed &&
contextManager.ScenarioContext().EffectiveExecutionStatus() == Result::passed)
{
const auto scopedStepReport = reportHandler.StepStart();
const auto scopedStepHook = hookExecution.StepStart();
Expand Down
4 changes: 2 additions & 2 deletions cucumber_cpp/library/engine/TestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ namespace cucumber_cpp::library::engine
{
}

void TestRunnerImpl::Run(const std::vector<std::unique_ptr<FeatureInfo>>& feature)
void TestRunnerImpl::Run(const std::vector<std::unique_ptr<FeatureInfo>>& features)
{
auto scope = testExecution.StartRun();

for (const auto& featurePtr : feature)
for (const auto& featurePtr : features)
RunFeature(*featurePtr);
}

Expand Down
41 changes: 14 additions & 27 deletions cucumber_cpp/library/engine/test/TestFailureHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@
#include "cucumber_cpp/library/engine/FailureHandler.hpp"
#include "cucumber_cpp/library/engine/Result.hpp"
#include "cucumber_cpp/library/engine/test_helper/ContextManagerInstance.hpp"
#include "cucumber_cpp/library/report/Report.hpp"
#include <cstddef>
#include <filesystem>
#include "cucumber_cpp/library/report/test_helper/ReportForwarderMock.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <optional>
#include <string>

namespace cucumber_cpp::library::engine
{
Expand All @@ -29,41 +25,32 @@ namespace cucumber_cpp::library::engine
CucumberAssertHelper{ testing::TestPartResult::kNonFatalFailure, "custom_file.cpp", 0, failure } = testing::Message() << user;
}

struct ReportForwarderMock : report::ReportForwarderImpl
{
using ReportForwarderImpl::ReportForwarderImpl;
virtual ~ReportForwarderMock() = default;

MOCK_METHOD(void, Failure, (const std::string& error, std::optional<std::filesystem::path> path, std::optional<std::size_t> line, std::optional<std::size_t> column), (override));
MOCK_METHOD(void, Error, (const std::string& error, std::optional<std::filesystem::path> path, std::optional<std::size_t> line, std::optional<std::size_t> column), (override));
};

struct TestFailureHandler : testing::Test
{

test_helper::ContextManagerInstance contextManager;
ReportForwarderMock reportHandler{ contextManager };
report::test_helper::ReportForwarderMock reportHandler{ contextManager };
TestAssertionHandlerImpl testAssertionHandler{ contextManager, reportHandler };
};
}

TEST_F(TestFailureHandler, SetContextToFailed)
{
ASSERT_THAT(contextManager.CurrentContext().ExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.ProgramContext().ExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.FeatureContext().ExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.RuleContext().ExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.ScenarioContext().ExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.StepContext().ExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.CurrentContext().EffectiveExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.ProgramContext().EffectiveExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.FeatureContext().EffectiveExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.RuleContext().EffectiveExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.ScenarioContext().EffectiveExecutionStatus(), testing::Eq(Result::passed));
ASSERT_THAT(contextManager.StepContext().EffectiveExecutionStatus(), testing::Eq(Result::passed));

ErrorWithFailureMessage("failure");

EXPECT_THAT(contextManager.CurrentContext().ExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.ProgramContext().ExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.FeatureContext().ExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.RuleContext().ExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.ScenarioContext().ExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.StepContext().ExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.CurrentContext().EffectiveExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.ProgramContext().EffectiveExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.FeatureContext().EffectiveExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.RuleContext().EffectiveExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.ScenarioContext().EffectiveExecutionStatus(), testing::Eq(Result::failed));
EXPECT_THAT(contextManager.StepContext().EffectiveExecutionStatus(), testing::Eq(Result::failed));
}

TEST_F(TestFailureHandler, ReportFailureMessage)
Expand Down
Loading
Loading