From de36995d3747297af3553ab48eb630b6bf8b0458 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Wed, 18 Mar 2026 08:44:26 +0000 Subject: [PATCH 01/20] feat: add parallel support --- .clang-tidy | 1 + .vscode/launch.json | 5 +- CMakeLists.txt | 14 +- CMakePresets.json | 15 ++ compatibility/BaseCompatibility.cpp | 2 +- cucumber_cpp/library/Application.cpp | 6 + cucumber_cpp/library/Application.hpp | 11 +- cucumber_cpp/library/engine/test/TestStep.cpp | 2 +- .../library/formatter/SummaryFormatter.cpp | 4 +- .../library/formatter/UsageFormatter.cpp | 2 +- .../library/formatter/helper/Theme.cpp | 6 - cucumber_cpp/library/query/Query.hpp | 2 +- cucumber_cpp/library/runtime/CMakeLists.txt | 16 ++ cucumber_cpp/library/runtime/MakeRuntime.cpp | 38 +++- cucumber_cpp/library/runtime/MakeRuntime.hpp | 3 - .../runtime/ParallelRuntimeAdapter.cpp | 170 ++++++++++++++++++ .../runtime/ParallelRuntimeAdapter.hpp | 44 +++++ .../library/runtime/SerialRuntimeAdapter.cpp | 38 +++- .../library/runtime/SerialRuntimeAdapter.hpp | 4 + cucumber_cpp/library/runtime/Worker.cpp | 56 +----- cucumber_cpp/library/runtime/Worker.hpp | 9 +- cucumber_cpp/library/support/Types.hpp | 8 +- cucumber_cpp/library/util/Broadcaster.cpp | 8 +- cucumber_cpp/library/util/Broadcaster.hpp | 16 +- external/CMakeLists.txt | 4 + external/jbaldwin/CMakeLists.txt | 1 + external/jbaldwin/libcoro/CMakeLists.txt | 16 ++ 27 files changed, 398 insertions(+), 103 deletions(-) create mode 100644 cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp create mode 100644 cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp create mode 100644 external/jbaldwin/CMakeLists.txt create mode 100644 external/jbaldwin/libcoro/CMakeLists.txt diff --git a/.clang-tidy b/.clang-tidy index 73ff6a07..ee77ff14 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -10,6 +10,7 @@ Checks: ' -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-special-member-functions, + -cppcoreguidelines-avoid-reference-coroutine-parameters, misc-*, -misc-no-recursion, -misc-non-private-member-variables-in-classes, diff --git a/.vscode/launch.json b/.vscode/launch.json index a043d84a..42a4b209 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -46,7 +46,10 @@ "args": [ "--format", "pretty", - "summary", + // "summary", + "--parallel", + "2", + "--fail-fast", "--retry", "1", "--tags", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b23b7f1..e866ab7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") project(cucumber-cpp-runner LANGUAGES C CXX VERSION 4.0.0) # x-release-please-version +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED On) +set(CMAKE_CXX_EXTENSIONS Off) + include(ccr_test_helpers) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -18,10 +22,12 @@ else() set(CCR_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL") endif() + option(CCR_FETCH_DEPS "Fetch dependencies via FetchContent." ${CCR_DEFAULTOPT} ) option(CCR_BUILD_TESTS "Enable build of the tests" ${CCR_DEFAULTOPT}) option(CCR_ENABLE_COVERAGE "Enable compiler flags for code coverage measurements" Off) option(CCR_ENABLE_TIME_PROFILE "Enable compiler flags for time profiling measurements" Off) +option(CCR_ENABLE_PARALLEL_SUPPORT "Enable parallel support through libcoro" Off) set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -57,10 +63,6 @@ if (CCR_ENABLE_TIME_PROFILE) endif() endif() -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED On) -set(CMAKE_CXX_EXTENSIONS Off) - set_directory_properties(PROPERTY USE_FOLDERS ON) include(FetchContent) @@ -78,6 +80,10 @@ else() find_package(cucumber_gherkin REQUIRED) find_package(fmt REQUIRED) + if(CCR_ENABLE_PARALLEL_SUPPORT) + find_package(libcoro REQUIRED) + endif() + if (CCR_BUILD_TESTS) find_package(yaml-cpp REQUIRED) endif() diff --git a/CMakePresets.json b/CMakePresets.json index c4b7b6c3..88c644b2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -32,6 +32,14 @@ "inherits": "defaults", "generator": "Ninja Multi-Config" }, + { + "name": "Host-Parallel", + "inherits": "Host", + "generator": "Ninja Multi-Config", + "cacheVariables": { + "CCR_ENABLE_PARALLEL_SUPPORT": "On" + } + }, { "name": "host-single-Debug", "displayName": "Configuration for Host Tooling and Tests, Single Config Generator, Debug", @@ -68,6 +76,13 @@ "CCACHE_COMPILERTYPE": "clang-cl" }, "generator": "Ninja Multi-Config" + }, + { + "name": "Windows-Parallel", + "inherits": "Windows", + "cacheVariables": { + "CCR_ENABLE_PARALLEL_SUPPORT": "On" + } } ], "buildPresets": [ diff --git a/compatibility/BaseCompatibility.cpp b/compatibility/BaseCompatibility.cpp index aae24f9c..d1ae0223 100644 --- a/compatibility/BaseCompatibility.cpp +++ b/compatibility/BaseCompatibility.cpp @@ -236,7 +236,7 @@ namespace compatibility auto contextStorageFactory{ std::make_shared() }; auto programContext{ std::make_unique(contextStorageFactory) }; - cucumber_cpp::library::util::Broadcaster broadcaster; + cucumber_cpp::library::util::BroadcasterImpl broadcaster; BroadcastListener broadcastListener{ devkit.ndjsonFile, devkit.ndjsonFile.parent_path() / "expected.ndjson", devkit.ndjsonFile.parent_path() / "actual.ndjson", broadcaster }; diff --git a/cucumber_cpp/library/Application.cpp b/cucumber_cpp/library/Application.cpp index 51516fcc..50657f3e 100644 --- a/cucumber_cpp/library/Application.cpp +++ b/cucumber_cpp/library/Application.cpp @@ -1,4 +1,5 @@ #include "cucumber_cpp/library/Application.hpp" +#include "CLI/CLI.hpp" #include "cucumber/gherkin/demangle.hpp" #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/Errors.hpp" @@ -119,6 +120,10 @@ namespace cucumber_cpp::library cli.add_flag("--feature-hooks,!--no-feature-hooks", options.featureHooks, "Run Before/After Feature hooks, note these are non-standard and are not supported by messages")->default_val(options.featureHooks); cli.add_flag("--recursive,!--no-recursive", options.recursive, "Search for feature files recursively")->default_val(options.recursive); +#if defined(ENABLE_PARALLEL_SUPPORT) + cli.add_option("--parallel", options.parallel, "Number of parallel workers to run scenarios. Default 0 (no parallelism)")->default_val(options.parallel); +#endif + CLI::deprecate_option(cli.add_option("--tag", options.tags, "Cucumber tag expression"), "-t,--tags"); cli.add_option("-t,--tags", options.tags, "Cucumber tag expression"); @@ -198,6 +203,7 @@ namespace cucumber_cpp::library .strict = options.strict, .retryTagExpression = tag_expression::Parse(fmt::to_string(fmt::join(options.retryTagFilter, " "))), .featureHooks = options.featureHooks, + .parallel = options.parallel, }, }; diff --git a/cucumber_cpp/library/Application.hpp b/cucumber_cpp/library/Application.hpp index 94cabed4..01e7dc1e 100644 --- a/cucumber_cpp/library/Application.hpp +++ b/cucumber_cpp/library/Application.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -45,8 +46,8 @@ namespace cucumber_cpp::library enum support::RunOptions::Ordering ordering{ support::RunOptions::Ordering::defined }; - std::size_t retry{ 0 }; - std::vector retryTagFilter{}; + std::uint32_t retry{ 0 }; + std::vector retryTagFilter; bool strict{ true }; @@ -54,7 +55,9 @@ namespace cucumber_cpp::library bool recursive{ true }; - std::vector tags{}; + std::vector tags; + + std::uint32_t parallel{ 0 }; }; explicit Application(std::shared_ptr contextStorageFactory = std::make_shared(), bool removeDefaultGoogleTestListener = true); @@ -80,7 +83,7 @@ namespace cucumber_cpp::library api::Formatters formatters; - util::Broadcaster broadcaster; + util::BroadcasterImpl broadcaster; cucumber_expression::ParameterRegistry parameterRegistry{ cucumber_cpp::library::support::DefinitionRegistration::Instance().GetRegisteredParameters() }; bool removeDefaultGoogleTestListener; diff --git a/cucumber_cpp/library/engine/test/TestStep.cpp b/cucumber_cpp/library/engine/test/TestStep.cpp index 046dc99a..7979c8b0 100644 --- a/cucumber_cpp/library/engine/test/TestStep.cpp +++ b/cucumber_cpp/library/engine/test/TestStep.cpp @@ -40,7 +40,7 @@ namespace cucumber_cpp::library::engine struct TestStep : testing::Test { - util::Broadcaster broadcaster; + util::BroadcasterImpl broadcaster; std::shared_ptr contextStorageFactory{ std::make_shared() }; Context context{ contextStorageFactory }; util::StepOrHookStarted stepOrHookStarted; diff --git a/cucumber_cpp/library/formatter/SummaryFormatter.cpp b/cucumber_cpp/library/formatter/SummaryFormatter.cpp index 121ee374..90fce5c6 100644 --- a/cucumber_cpp/library/formatter/SummaryFormatter.cpp +++ b/cucumber_cpp/library/formatter/SummaryFormatter.cpp @@ -181,7 +181,7 @@ namespace cucumber_cpp::library::formatter const auto& testStepFinishedAndTestStep = query.FindTestStepFinishedAndTestStepBy(testCaseStarted); auto isBeforeHook = true; - for (const auto [testStepFinished, testStep] : testStepFinishedAndTestStep) + for (const auto& [testStepFinished, testStep] : testStepFinishedAndTestStep) { if (testStep->hook_id.has_value()) HandleHookStep(stream, query, *testStepFinished, *testStep, scenarioIndent, maxContentLength, isBeforeHook, useStatusIcon, theme); @@ -201,8 +201,6 @@ namespace cucumber_cpp::library::formatter const auto& pickle = query.FindPickleBy(testCaseStarted); const auto& lineage = query.FindLineageByPickle(pickle); const auto& scenario = lineage.scenario; - const auto& rule = lineage.rule; - const auto& feature = lineage.feature; const auto& testCase = query.FindTestCaseBy(testCaseStarted); const auto maxContentLength = CalculateLength(query, pickle, testCaseStarted, testCaseFinished, *scenario, testCase, useStatusIcon, theme); diff --git a/cucumber_cpp/library/formatter/UsageFormatter.cpp b/cucumber_cpp/library/formatter/UsageFormatter.cpp index 51de86ba..878f4144 100644 --- a/cucumber_cpp/library/formatter/UsageFormatter.cpp +++ b/cucumber_cpp/library/formatter/UsageFormatter.cpp @@ -151,7 +151,7 @@ namespace cucumber_cpp::library::formatter const auto& testCase = query.FindTestCaseBy(testCaseStarted); const auto testStepFinishedAndTestStep = query.FindTestStepFinishedAndTestStepBy(testCaseStarted); - for (const auto [testStepFinished, testStep] : testStepFinishedAndTestStep) + for (const auto& [testStepFinished, testStep] : testStepFinishedAndTestStep) AddUsageMatchToMapping(query, *testStepFinished, *testStep, testCase, mapping); } } diff --git a/cucumber_cpp/library/formatter/helper/Theme.cpp b/cucumber_cpp/library/formatter/helper/Theme.cpp index 0021d621..f01c308c 100644 --- a/cucumber_cpp/library/formatter/helper/Theme.cpp +++ b/cucumber_cpp/library/formatter/helper/Theme.cpp @@ -3,7 +3,6 @@ #include "fmt/color.h" #include #include -#include #include #include #include @@ -42,11 +41,6 @@ namespace cucumber_cpp::library::formatter::helper { cucumber::messages::test_step_result_status::UNKNOWN, "?" }, }; - std::optional GetColorStyle(std::optional def) - { - return def; - } - const std::regex ansiEscape{ "\033\\[[^m]+m" }; } diff --git a/cucumber_cpp/library/query/Query.hpp b/cucumber_cpp/library/query/Query.hpp index 586ac8e2..73d25518 100644 --- a/cucumber_cpp/library/query/Query.hpp +++ b/cucumber_cpp/library/query/Query.hpp @@ -112,7 +112,7 @@ namespace cucumber_cpp::library::query }; struct Query - : util::Broadcaster + : util::BroadcasterImpl , util::Listener { explicit Query(util::Broadcaster& broadcaster); diff --git a/cucumber_cpp/library/runtime/CMakeLists.txt b/cucumber_cpp/library/runtime/CMakeLists.txt index ce295e84..1787a7ba 100644 --- a/cucumber_cpp/library/runtime/CMakeLists.txt +++ b/cucumber_cpp/library/runtime/CMakeLists.txt @@ -27,6 +27,22 @@ target_link_libraries(cucumber_cpp.library.runtime PUBLIC fmt::fmt ) +if (CCR_ENABLE_PARALLEL_SUPPORT) + target_sources(cucumber_cpp.library.runtime PRIVATE + ParallelRuntimeAdapter.cpp + ParallelRuntimeAdapter.hpp + ) + + target_link_libraries(cucumber_cpp.library.runtime PUBLIC + libcoro + ) + + target_compile_definitions(cucumber_cpp.library.runtime PUBLIC + ENABLE_PARALLEL_SUPPORT + ) +endif() + + if (CCR_BUILD_TESTS) add_subdirectory(test) # add_subdirectory(test_helper) diff --git a/cucumber_cpp/library/runtime/MakeRuntime.cpp b/cucumber_cpp/library/runtime/MakeRuntime.cpp index 04b88d83..0af71e53 100644 --- a/cucumber_cpp/library/runtime/MakeRuntime.cpp +++ b/cucumber_cpp/library/runtime/MakeRuntime.cpp @@ -9,19 +9,39 @@ #include #include #include +#include + +#if defined(ENABLE_PARALLEL_SUPPORT) +#include "cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp" +#endif namespace cucumber_cpp::library::runtime { - std::unique_ptr MakeAdapter(const support::RunOptions::Runtime& options, std::string testRunStartedId, util::Broadcaster& broadcaster, const std::list& sourcedPickles, support::SupportCodeLibrary& supportCodeLibrary, cucumber::gherkin::id_generator_ptr idGenerator, Context& programContext) + namespace { - return std::make_unique( - testRunStartedId, - broadcaster, - idGenerator, - sourcedPickles, - options, - supportCodeLibrary, - programContext); + std::unique_ptr MakeAdapter(const support::RunOptions::Runtime& options, std::string testRunStartedId, util::Broadcaster& broadcaster, const std::list& sourcedPickles, support::SupportCodeLibrary& supportCodeLibrary, cucumber::gherkin::id_generator_ptr idGenerator, Context& programContext) + { +#if defined(ENABLE_PARALLEL_SUPPORT) + if (options.parallel > 0) + return std::make_unique( + std::move(testRunStartedId), + broadcaster, + std::move(idGenerator), + sourcedPickles, + options, + supportCodeLibrary, + programContext); + else +#endif + return std::make_unique( + std::move(testRunStartedId), + broadcaster, + std::move(idGenerator), + sourcedPickles, + options, + supportCodeLibrary, + programContext); + } } std::unique_ptr MakeRuntime(const support::RunOptions::Runtime& options, util::Broadcaster& broadcaster, const std::list& sourcedPickles, support::SupportCodeLibrary& supportCodeLibrary, cucumber::gherkin::id_generator_ptr idGenerator, Context& programContext) diff --git a/cucumber_cpp/library/runtime/MakeRuntime.hpp b/cucumber_cpp/library/runtime/MakeRuntime.hpp index 3b3f5953..ab034f92 100644 --- a/cucumber_cpp/library/runtime/MakeRuntime.hpp +++ b/cucumber_cpp/library/runtime/MakeRuntime.hpp @@ -8,12 +8,9 @@ #include "cucumber_cpp/library/util/Broadcaster.hpp" #include #include -#include namespace cucumber_cpp::library::runtime { - std::unique_ptr MakeAdapter(const support::RunOptions::Runtime& options, std::string testRunStartedId, util::Broadcaster& broadcaster, support::SupportCodeLibrary& supportCodeLibrary, cucumber::gherkin::id_generator_ptr idGenerator, Context& programContext); - std::unique_ptr MakeRuntime(const support::RunOptions::Runtime& options, util::Broadcaster& broadcaster, const std::list& sourcedPickles, support::SupportCodeLibrary& supportCodeLibrary, cucumber::gherkin::id_generator_ptr idGenerator, Context& programContext); } diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp new file mode 100644 index 00000000..68d6a000 --- /dev/null +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp @@ -0,0 +1,170 @@ +#include "cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp" +#include ".build/Host-Iwyu/_deps/libfmt-src/include/fmt/format.h" +#include "coro/task.hpp" +#include "coro/thread_pool.hpp" +#include "cucumber/gherkin/id_generator.hpp" +#include "cucumber/messages/envelope.hpp" +#include "cucumber/messages/gherkin_document.hpp" +#include "cucumber/messages/test_step_result_status.hpp" +#include "cucumber_cpp/library/Context.hpp" +#include "cucumber_cpp/library/assemble/AssembleTestSuites.hpp" +#include "cucumber_cpp/library/assemble/AssembledTestCase.hpp" +#include "cucumber_cpp/library/assemble/AssembledTestSuite.hpp" +#include "cucumber_cpp/library/runtime/Worker.hpp" +#include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" +#include "cucumber_cpp/library/support/Types.hpp" +#include "cucumber_cpp/library/util/Broadcaster.hpp" +#include "cucumber_cpp/library/util/GetWorstTestStepResult.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cucumber_cpp::library::runtime +{ + namespace + { + bool IsFailing(cucumber::messages::test_step_result_status status, bool dryRun) + { + if (dryRun) + return false; + + return status != cucumber::messages::test_step_result_status::PASSED; + } + + coro::task RunTestCase(std::unique_ptr& tp, coro::latch& tasksLatch, runtime::Worker& worker, const cucumber::messages::gherkin_document& gherkinDocument, const assemble::AssembledTestCase& assembledTestCase, Context& testSuiteContext, bool& failing) + { + co_await tp->schedule(); + + try + { + failing |= !worker.RunTestCase(gherkinDocument, assembledTestCase, testSuiteContext, failing); + } + catch (...) + { + failing = true; + } + + tasksLatch.count_down(); + + co_return; + } + + struct ParallelBroadcaster : util::Broadcaster + { + ParallelBroadcaster(util::Broadcaster& broadcaster, std::unique_ptr& threadPool) + : broadcaster{ broadcaster } + , threadPool{ threadPool } + { + } + + void AddListener(util::Listener* listener) override + { + broadcaster.AddListener(listener); + } + + void RemoveListener(util::Listener* listener) override + { + broadcaster.RemoveListener(listener); + } + + void BroadcastEvent(const cucumber::messages::envelope& envelope) override + { + coro::sync_wait(queue.emplace(envelope)); + } + + coro::task BroadcastEventTask() + { + co_await threadPool->schedule(); + + while (true) + { + auto event = co_await queue.pop(); + + if (!event) + break; + + broadcaster.BroadcastEvent(*event); + } + } + + coro::task ShutdownTask(coro::latch& latch) + { + co_await threadPool->schedule(); + co_await latch; + co_await queue.shutdown_drain(threadPool); + co_return; + } + + private: + util::Broadcaster& broadcaster; + std::unique_ptr& threadPool; + + coro::queue queue; + }; + } + + ParallelRuntimeAdapter::ParallelRuntimeAdapter(std::string testRunStartedId, + util::Broadcaster& broadcaster, + cucumber::gherkin::id_generator_ptr idGenerator, + const std::list& sourcedPickles, + const support::RunOptions::Runtime& options, + support::SupportCodeLibrary& supportCodeLibrary, + Context& programContext) + : testRunStartedId{ std::move(testRunStartedId) } + , broadcaster{ broadcaster } + , idGenerator{ std::move(idGenerator) } + , sourcedPickles{ sourcedPickles } + , options{ options } + , supportCodeLibrary{ supportCodeLibrary } + , programContext{ programContext } + , threadPool{ coro::thread_pool::make_unique(coro::thread_pool::options{ .thread_count = options.parallel }) } + { + } + + bool ParallelRuntimeAdapter::Run() + { + std::vector> tasks; + bool failing = false; + runtime::Worker synchronousWorker{ testRunStartedId, broadcaster, idGenerator, options, supportCodeLibrary, programContext }; + + failing |= IsFailing(util::GetWorstTestStepResult(synchronousWorker.RunBeforeAllHooks()).status, options.dryRun); + + if (!failing) + { + ParallelBroadcaster parallelBroadcaster{ broadcaster, threadPool }; + tasks.emplace_back(parallelBroadcaster.BroadcastEventTask()); + + runtime::Worker parallelWorker{ testRunStartedId, parallelBroadcaster, idGenerator, options, supportCodeLibrary, programContext }; + + auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, idGenerator); + + coro::latch taskLatch{ 10 * std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) + { + return suite.testCases; + }) | + std::views::join) }; + + for (const auto& assembledTestSuite : assembledTestSuites) + for (const auto& assembledTestCase : assembledTestSuite.testCases) + for (auto i{ 0 }; i < 10; ++i) + tasks.emplace_back(RunTestCase(threadPool, taskLatch, parallelWorker, assembledTestSuite.gherkinDocument, assembledTestCase, programContext, failing)); + + tasks.emplace_back(parallelBroadcaster.ShutdownTask(taskLatch)); + + coro::sync_wait(coro::when_all(std::move(tasks))); + } + + failing |= IsFailing(util::GetWorstTestStepResult(synchronousWorker.RunAfterAllHooks()).status, options.dryRun); + + return !failing; + } +} diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp new file mode 100644 index 00000000..5ffa1590 --- /dev/null +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp @@ -0,0 +1,44 @@ +#ifndef RUNTIME_PARALLEL_RUNTIME_ADAPTER_HPP +#define RUNTIME_PARALLEL_RUNTIME_ADAPTER_HPP + +#include "cucumber/gherkin/id_generator.hpp" +#include "cucumber_cpp/library/Context.hpp" +#include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" +#include "cucumber_cpp/library/support/Types.hpp" +#include "cucumber_cpp/library/util/Broadcaster.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace cucumber_cpp::library::runtime +{ + struct ParallelRuntimeAdapter : support::RuntimeAdapter + { + ParallelRuntimeAdapter(std::string testRunStartedId, + util::Broadcaster& broadcaster, + cucumber::gherkin::id_generator_ptr idGenerator, + const std::list& sourcedPickles, + const support::RunOptions::Runtime& options, + support::SupportCodeLibrary& supportCodeLibrary, + Context& programContext); + + bool Run() override; + + private: + std::string testRunStartedId; + util::Broadcaster& broadcaster; + cucumber::gherkin::id_generator_ptr idGenerator; + const std::list& sourcedPickles; + const support::RunOptions::Runtime& options; + support::SupportCodeLibrary& supportCodeLibrary; + Context& programContext; + + std::unique_ptr threadPool; + }; +} + +#endif diff --git a/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp index c5249c1e..a0b9ee45 100644 --- a/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp +++ b/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp @@ -3,6 +3,7 @@ #include "cucumber/messages/test_step_result_status.hpp" #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/assemble/AssembleTestSuites.hpp" +#include "cucumber_cpp/library/assemble/AssembledTestSuite.hpp" #include "cucumber_cpp/library/runtime/Worker.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" #include "cucumber_cpp/library/support/Types.hpp" @@ -11,6 +12,7 @@ #include #include #include +#include namespace cucumber_cpp::library::runtime { @@ -32,9 +34,9 @@ namespace cucumber_cpp::library::runtime const support::RunOptions::Runtime& options, support::SupportCodeLibrary& supportCodeLibrary, Context& programContext) - : testRunStartedId{ testRunStartedId } + : testRunStartedId{ std::move(testRunStartedId) } , broadcaster{ broadcaster } - , idGenerator{ idGenerator } + , idGenerator{ std::move(idGenerator) } , sourcedPickles{ sourcedPickles } , options{ options } , supportCodeLibrary{ supportCodeLibrary } @@ -56,10 +58,9 @@ namespace cucumber_cpp::library::runtime auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, idGenerator); for (const auto& assembledTestSuite : assembledTestSuites) - { try { - const auto success = worker.RunTestSuite(assembledTestSuite, failing); + const auto success = RunTestSuite(worker, assembledTestSuite, failing); if (!success) failing = true; } @@ -67,7 +68,6 @@ namespace cucumber_cpp::library::runtime { failing = true; } - } } const auto afterHookResults = worker.RunAfterAllHooks(); @@ -77,4 +77,32 @@ namespace cucumber_cpp::library::runtime return !failing; } + + bool SerialRuntimeAdapter::RunTestSuite(runtime::Worker& worker, const assemble::AssembledTestSuite& assembledTestSuite, bool failing) + { + Context testSuiteContext{ &programContext }; + + auto failed = false; + + if (options.featureHooks) + { + const auto beforeHookResults = worker.RunBeforeTestSuiteHooks(*assembledTestSuite.gherkinDocument.feature, testSuiteContext); + + if (IsFailing(util::GetWorstTestStepResult(beforeHookResults).status, options.dryRun)) + failing = true; + } + + for (const auto& assembledTestCase : assembledTestSuite.testCases) + failed |= !worker.RunTestCase(assembledTestSuite.gherkinDocument, assembledTestCase, testSuiteContext, failed || failing); + + if (options.featureHooks) + { + const auto afterHookResults = worker.RunAfterTestSuiteHooks(*assembledTestSuite.gherkinDocument.feature, testSuiteContext); + + if (IsFailing(util::GetWorstTestStepResult(afterHookResults).status, options.dryRun)) + failing = true; + } + + return !failed; + } } diff --git a/cucumber_cpp/library/runtime/SerialRuntimeAdapter.hpp b/cucumber_cpp/library/runtime/SerialRuntimeAdapter.hpp index 0321b3ec..d20f07be 100644 --- a/cucumber_cpp/library/runtime/SerialRuntimeAdapter.hpp +++ b/cucumber_cpp/library/runtime/SerialRuntimeAdapter.hpp @@ -3,6 +3,8 @@ #include "cucumber/gherkin/id_generator.hpp" #include "cucumber_cpp/library/Context.hpp" +#include "cucumber_cpp/library/assemble/AssembledTestSuite.hpp" +#include "cucumber_cpp/library/runtime/Worker.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" #include "cucumber_cpp/library/support/Types.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" @@ -24,6 +26,8 @@ namespace cucumber_cpp::library::runtime bool Run() override; private: + bool RunTestSuite(runtime::Worker& worker, const assemble::AssembledTestSuite& assembledTestSuite, bool failing); + std::string testRunStartedId; util::Broadcaster& broadcaster; cucumber::gherkin::id_generator_ptr idGenerator; diff --git a/cucumber_cpp/library/runtime/Worker.cpp b/cucumber_cpp/library/runtime/Worker.cpp index 2729591d..bc892490 100644 --- a/cucumber_cpp/library/runtime/Worker.cpp +++ b/cucumber_cpp/library/runtime/Worker.cpp @@ -10,14 +10,11 @@ #include "cucumber/messages/test_step_result_status.hpp" #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/assemble/AssembledTestCase.hpp" -#include "cucumber_cpp/library/assemble/AssembledTestSuite.hpp" #include "cucumber_cpp/library/runtime/TestCaseRunner.hpp" -#include "cucumber_cpp/library/support/Body.hpp" #include "cucumber_cpp/library/support/HookRegistry.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" #include "cucumber_cpp/library/support/Types.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" -#include "cucumber_cpp/library/util/GetWorstTestStepResult.hpp" #include "cucumber_cpp/library/util/HookData.hpp" #include "cucumber_cpp/library/util/Timestamp.hpp" #include "cucumber_cpp/library/util/TransformHookData.hpp" @@ -28,7 +25,6 @@ #include "fmt/format.h" #include #include -#include #include #include #include @@ -49,21 +45,11 @@ namespace cucumber_cpp::library::runtime std::size_t RetriesForPickle(const cucumber::messages::pickle& pickle, const support::RunOptions::Runtime& options) { - if (options.retry == 0) - return 0; - else if (options.retryTagExpression->Evaluate(util::TransformPickleTags(pickle.tags))) + if (options.retry != 0 && options.retryTagExpression->Evaluate(util::TransformPickleTags(pickle.tags))) return options.retry; else return 0; } - - bool IsFailing(cucumber::messages::test_step_result_status status, bool dryRun) - { - if (dryRun) - return false; - - return status != cucumber::messages::test_step_result_status::PASSED; - } } Worker::Worker(std::string_view testRunStartedId, @@ -103,34 +89,6 @@ namespace cucumber_cpp::library::runtime return results; } - bool Worker::RunTestSuite(const assemble::AssembledTestSuite& assembledTestSuite, bool failing) - { - Context testSuiteContext{ &programContext }; - - auto failed = false; - - if (options.featureHooks) - { - const auto beforeHookResults = RunBeforeTestSuiteHooks(*assembledTestSuite.gherkinDocument.feature, testSuiteContext); - - if (IsFailing(util::GetWorstTestStepResult(beforeHookResults).status, options.dryRun)) - failing = true; - } - - for (const auto& assembledTestCase : assembledTestSuite.testCases) - failed |= !RunTestCase(assembledTestSuite.gherkinDocument, assembledTestCase, testSuiteContext, failed || failing); - - if (options.featureHooks) - { - const auto afterHookResults = RunAfterTestSuiteHooks(*assembledTestSuite.gherkinDocument.feature, testSuiteContext); - - if (IsFailing(util::GetWorstTestStepResult(afterHookResults).status, options.dryRun)) - failing = true; - } - - return !failed; - } - bool Worker::RunTestCase(const cucumber::messages::gherkin_document& gherkinDocument, const assemble::AssembledTestCase& assembledTestCase, Context& testSuiteContext, bool failing) { TestCaseRunner testCaseRunner{ @@ -186,7 +144,7 @@ namespace cucumber_cpp::library::runtime .timestamp = util::TimestampNow(), }; - broadcaster.BroadcastEvent({ .test_run_hook_started = testRunHookStarted }); + broadcaster.BroadcastEvent(cucumber::messages::envelope{ .test_run_hook_started = testRunHookStarted }); cucumber::messages::test_step_result result{ .duration{ .seconds = 0, .nanos = 0 }, .status = cucumber::messages::test_step_result_status::SKIPPED }; if (!options.dryRun) @@ -197,11 +155,11 @@ namespace cucumber_cpp::library::runtime throw GlobalHookError{ fmt::format("Global Hook Failed: {}\nresult:{}", util::TransformHookData(definition.data).to_string(), result.to_string()) }; } - broadcaster.BroadcastEvent({ .test_run_hook_finished = cucumber::messages::test_run_hook_finished{ - .test_run_hook_started_id = testRunHookStartedId, - .result = result, - .timestamp = util::TimestampNow(), - } }); + broadcaster.BroadcastEvent(cucumber::messages::envelope{ .test_run_hook_finished = cucumber::messages::test_run_hook_finished{ + .test_run_hook_started_id = testRunHookStartedId, + .result = result, + .timestamp = util::TimestampNow(), + } }); return result; } diff --git a/cucumber_cpp/library/runtime/Worker.hpp b/cucumber_cpp/library/runtime/Worker.hpp index bf22a53e..3b864440 100644 --- a/cucumber_cpp/library/runtime/Worker.hpp +++ b/cucumber_cpp/library/runtime/Worker.hpp @@ -37,16 +37,15 @@ namespace cucumber_cpp::library::runtime std::vector RunBeforeAllHooks(); std::vector RunAfterAllHooks(); - bool RunTestSuite(const assemble::AssembledTestSuite& assembledTestSuite, bool failing); - bool RunTestCase(const cucumber::messages::gherkin_document& gherkinDocument, const assemble::AssembledTestCase& assembledTestCase, Context& testSuiteContext, bool failing); - - private: std::vector RunBeforeTestSuiteHooks(const cucumber::messages::feature& feature, Context& context); std::vector RunAfterTestSuiteHooks(const cucumber::messages::feature& feature, Context& context); + bool RunTestCase(const cucumber::messages::gherkin_document& gherkinDocument, const assemble::AssembledTestCase& assembledTestCase, Context& testSuiteContext, bool failing); + + private: cucumber::messages::test_step_result RunTestHook(const std::string& id, Context& context); - bool IsStatusFailed(cucumber::messages::test_step_result_status status) const; + [[nodiscard]] bool IsStatusFailed(cucumber::messages::test_step_result_status status) const; std::string_view testRunStartedId; util::Broadcaster& broadcaster; diff --git a/cucumber_cpp/library/support/Types.hpp b/cucumber_cpp/library/support/Types.hpp index 865aaa08..cc0ea2b3 100644 --- a/cucumber_cpp/library/support/Types.hpp +++ b/cucumber_cpp/library/support/Types.hpp @@ -6,6 +6,7 @@ #include "cucumber/messages/pickle.hpp" #include "cucumber_cpp/library/tag_expression/Model.hpp" #include +#include #include #include #include @@ -17,7 +18,7 @@ namespace cucumber_cpp::library::support { struct RunOptions { - enum class Ordering + enum class Ordering : std::uint8_t { defined, reverse, @@ -25,7 +26,7 @@ namespace cucumber_cpp::library::support struct Sources { - std::set> paths{}; + std::set> paths; std::unique_ptr tagExpression; Ordering ordering{ Ordering::defined }; @@ -38,8 +39,9 @@ namespace cucumber_cpp::library::support bool failGlobalHookFast{ false }; std::size_t retry{ 0 }; bool strict{ true }; - std::unique_ptr retryTagExpression{}; + std::unique_ptr retryTagExpression; bool featureHooks{ false }; + std::uint32_t parallel{ 0 }; } runtime; struct RunEnvironment diff --git a/cucumber_cpp/library/util/Broadcaster.cpp b/cucumber_cpp/library/util/Broadcaster.cpp index a3932f20..d9e25226 100644 --- a/cucumber_cpp/library/util/Broadcaster.cpp +++ b/cucumber_cpp/library/util/Broadcaster.cpp @@ -23,19 +23,19 @@ namespace cucumber_cpp::library::util onEvent(envelope); } - void Broadcaster::AddListener(Listener* listener) + void BroadcasterImpl::AddListener(Listener* listener) { listeners.push_back(listener); } - void Broadcaster::RemoveListener(Listener* listener) + void BroadcasterImpl::RemoveListener(Listener* listener) { std::erase(listeners, listener); } - void Broadcaster::BroadcastEvent(const cucumber::messages::envelope& envelope) + void BroadcasterImpl::BroadcastEvent(const cucumber::messages::envelope& envelope) { - for (auto& listener : listeners) + for (const auto& listener : listeners) listener->Invoke(envelope); } } diff --git a/cucumber_cpp/library/util/Broadcaster.hpp b/cucumber_cpp/library/util/Broadcaster.hpp index 15f55281..4ff9a961 100644 --- a/cucumber_cpp/library/util/Broadcaster.hpp +++ b/cucumber_cpp/library/util/Broadcaster.hpp @@ -33,10 +33,20 @@ namespace cucumber_cpp::library::util struct Broadcaster { - void AddListener(Listener* listener); - void RemoveListener(Listener* listener); + virtual ~Broadcaster() = default; - void BroadcastEvent(const cucumber::messages::envelope& envelope); + virtual void AddListener(Listener* listener) = 0; + virtual void RemoveListener(Listener* listener) = 0; + + virtual void BroadcastEvent(const cucumber::messages::envelope& envelope) = 0; + }; + + struct BroadcasterImpl : Broadcaster + { + void AddListener(Listener* listener) override; + void RemoveListener(Listener* listener) override; + + void BroadcastEvent(const cucumber::messages::envelope& envelope) override; private: std::vector listeners; diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 6de30b50..99c5ebd6 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -12,3 +12,7 @@ if (CCR_BUILD_TESTS) endif() add_subdirectory(tobiaslocker) add_subdirectory(zeux) + +if (CCR_ENABLE_PARALLEL_SUPPORT) + add_subdirectory(jbaldwin) +endif() diff --git a/external/jbaldwin/CMakeLists.txt b/external/jbaldwin/CMakeLists.txt new file mode 100644 index 00000000..04f36133 --- /dev/null +++ b/external/jbaldwin/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(libcoro) diff --git a/external/jbaldwin/libcoro/CMakeLists.txt b/external/jbaldwin/libcoro/CMakeLists.txt new file mode 100644 index 00000000..3bc6c86f --- /dev/null +++ b/external/jbaldwin/libcoro/CMakeLists.txt @@ -0,0 +1,16 @@ +add_compile_options(-Wno-missing-field-initializers) + +FetchContent_Declare( + libcoro + GIT_REPOSITORY https://github.com/jbaldwin/libcoro.git + GIT_TAG 0161911f260a7507ac1dd4aa26da2e2c092fb550 # v0.16.0 + SYSTEM +) + +set(LIBCORO_FEATURE_TLS OFF CACHE STRING "") +set(LIBCORO_BUILD_TESTS OFF CACHE STRING "") +set(LIBCORO_BUILD_EXAMPLES OFF CACHE STRING "") + +FetchContent_MakeAvailable(libcoro) + +target_compile_options(libcoro INTERFACE $<$:-Wno-extra>) From 595226489658930de701934bb28c6fb41321461a Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Wed, 18 Mar 2026 12:37:45 +0000 Subject: [PATCH 02/20] Add feature hooks support and corresponding tests --- .../test_optional_feature_hooks.feature | 4 ++++ cucumber_cpp/acceptance_test/hooks/Hooks.cpp | 11 ++++++++++ cucumber_cpp/acceptance_test/test.bats | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 cucumber_cpp/acceptance_test/features/test_optional_feature_hooks.feature diff --git a/cucumber_cpp/acceptance_test/features/test_optional_feature_hooks.feature b/cucumber_cpp/acceptance_test/features/test_optional_feature_hooks.feature new file mode 100644 index 00000000..e37c6823 --- /dev/null +++ b/cucumber_cpp/acceptance_test/features/test_optional_feature_hooks.feature @@ -0,0 +1,4 @@ +@featurehook +Feature: Test feature hook bindings + Scenario: Run feature hooks before and after + Given a given step diff --git a/cucumber_cpp/acceptance_test/hooks/Hooks.cpp b/cucumber_cpp/acceptance_test/hooks/Hooks.cpp index de7fc35e..605fc040 100644 --- a/cucumber_cpp/acceptance_test/hooks/Hooks.cpp +++ b/cucumber_cpp/acceptance_test/hooks/Hooks.cpp @@ -1,4 +1,5 @@ #include "cucumber_cpp/CucumberCpp.hpp" +#include "cucumber_cpp/Steps.hpp" #include "gmock/gmock.h" #include #include @@ -17,6 +18,16 @@ HOOK_AFTER_ALL() std::cout << "HOOK_AFTER_ALL\n"; } +HOOK_BEFORE_FEATURE("@featurehook") +{ + std::cout << "HOOK_BEFORE_FEATURE\n"; +} + +HOOK_AFTER_FEATURE("@featurehook") +{ + std::cout << "HOOK_AFTER_FEATURE\n"; +} + HOOK_BEFORE_SCENARIO("@scenariohook and @bats") { std::cout << "HOOK_BEFORE_SCENARIO\n"; diff --git a/cucumber_cpp/acceptance_test/test.bats b/cucumber_cpp/acceptance_test/test.bats index d7ba7f2f..e94a167a 100644 --- a/cucumber_cpp/acceptance_test/test.bats +++ b/cucumber_cpp/acceptance_test/test.bats @@ -222,3 +222,24 @@ teardown() { assert_output --partial "| this step is used | - |" assert_output --partial "| This step is unused | UNUSED |" } + +@test "Test feature hooks enabled" { + run $acceptance_test --tags "@featurehook" --feature-hooks -- cucumber_cpp/acceptance_test/features + assert_success + assert_output --partial "HOOK_BEFORE_FEATURE" + assert_output --partial "HOOK_AFTER_FEATURE" +} + +@test "Test feature hooks disabled by default" { + run $acceptance_test --tags "@featurehook" -- cucumber_cpp/acceptance_test/features + assert_success + refute_output --partial "HOOK_BEFORE_FEATURE" + refute_output --partial "HOOK_AFTER_FEATURE" +} + +@test "Test feature hooks disabled explicitly" { + run $acceptance_test --tags "@featurehook" --no-feature-hooks -- cucumber_cpp/acceptance_test/features + assert_success + refute_output --partial "HOOK_BEFORE_FEATURE" + refute_output --partial "HOOK_AFTER_FEATURE" +} From f583896d2533b53bc02a7a902b67d0ecc4223e86 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Thu, 19 Mar 2026 09:46:10 +0000 Subject: [PATCH 03/20] refactor: rename parallel presets to asynchronous and add new configurations --- CMakePresets.json | 15 +++++++++++++-- .../library/runtime/ParallelRuntimeAdapter.cpp | 3 +-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 88c644b2..f7bee11c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,7 +33,7 @@ "generator": "Ninja Multi-Config" }, { - "name": "Host-Parallel", + "name": "Host-Asynchronous", "inherits": "Host", "generator": "Ninja Multi-Config", "cacheVariables": { @@ -78,7 +78,7 @@ "generator": "Ninja Multi-Config" }, { - "name": "Windows-Parallel", + "name": "Windows-Asynchronous", "inherits": "Windows", "cacheVariables": { "CCR_ENABLE_PARALLEL_SUPPORT": "On" @@ -101,6 +101,17 @@ "configurePreset": "Host", "jobs": 4 }, + { + "name": "Host-Asynchronous-Release", + "configuration": "Release", + "configurePreset": "Host-Asynchronous" + }, + { + "name": "Host-Asynchronous-Debug", + "configuration": "Debug", + "configurePreset": "Host-Asynchronous", + "jobs": 4 + }, { "name": "Host-Iwyu-Debug", "configuration": "Debug", diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp index 68d6a000..d6466413 100644 --- a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp @@ -1,5 +1,4 @@ #include "cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp" -#include ".build/Host-Iwyu/_deps/libfmt-src/include/fmt/format.h" #include "coro/task.hpp" #include "coro/thread_pool.hpp" #include "cucumber/gherkin/id_generator.hpp" @@ -147,7 +146,7 @@ namespace cucumber_cpp::library::runtime auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, idGenerator); - coro::latch taskLatch{ 10 * std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) + coro::latch taskLatch{ 10 * std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) -> std::list& { return suite.testCases; }) | From 15dd3736401187ab3cafa741db0a3d2165da44bc Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 09:11:49 +0000 Subject: [PATCH 04/20] fix: change return type to const for test case list in task latch initialization --- cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp index d6466413..f1df9ea0 100644 --- a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp @@ -146,7 +146,7 @@ namespace cucumber_cpp::library::runtime auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, idGenerator); - coro::latch taskLatch{ 10 * std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) -> std::list& + coro::latch taskLatch{ 10 * std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) -> const std::list& { return suite.testCases; }) | From 0d282996afcebe19c6189fd06d25d578ca3b8f2d Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 09:12:05 +0000 Subject: [PATCH 05/20] fix: remove unnecessary loop in task creation for test cases --- cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp index f1df9ea0..e86bb87c 100644 --- a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp @@ -154,8 +154,7 @@ namespace cucumber_cpp::library::runtime for (const auto& assembledTestSuite : assembledTestSuites) for (const auto& assembledTestCase : assembledTestSuite.testCases) - for (auto i{ 0 }; i < 10; ++i) - tasks.emplace_back(RunTestCase(threadPool, taskLatch, parallelWorker, assembledTestSuite.gherkinDocument, assembledTestCase, programContext, failing)); + tasks.emplace_back(RunTestCase(threadPool, taskLatch, parallelWorker, assembledTestSuite.gherkinDocument, assembledTestCase, programContext, failing)); tasks.emplace_back(parallelBroadcaster.ShutdownTask(taskLatch)); From 6fdada50644efdf2ec7fbb75ccf4af4ca0c1bd8b Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 12:37:59 +0000 Subject: [PATCH 06/20] feat: add asynchronous support and update CI configurations --- .github/workflows/ci.yml | 4 ++-- CMakePresets.json | 7 +++++-- cucumber_cpp/acceptance_test/steps/Steps.cpp | 2 +- cucumber_cpp/acceptance_test/test.bats | 10 ++++++++++ .../library/runtime/ParallelRuntimeAdapter.cpp | 18 ++++++++++-------- .../library/runtime/ParallelRuntimeAdapter.hpp | 7 ------- 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1482a4f6..c03ba383 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: container: ghcr.io/philips-software/amp-devcontainer-cpp:v6.8.2@sha256:8d6d0b49bef9b1f572793dee8dcc05edcbe4f44f108f075640dda284ff3e2d4e # v6.8.2 strategy: matrix: - target: [Host, Windows] + target: [Host, Host-Asynchronous, Windows] fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -81,7 +81,7 @@ jobs: matrix: os: [macos, windows, ubuntu] version: [latest] - type: [synchronous] + type: [synchronous, asynchronous] fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/CMakePresets.json b/CMakePresets.json index 089f91f0..e60d3ad2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -24,7 +24,8 @@ "generator": "Ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "CCR_ENABLE_COVERAGE": "On" + "CCR_ENABLE_COVERAGE": "On", + "CCR_ENABLE_PARALLEL_SUPPORT": "On" } }, { @@ -217,7 +218,9 @@ "name": "defaults", "hidden": true, "output": { - "outputOnFailure": true + "outputOnFailure": true, + "verbosity": "extra", + "debug": true }, "execution": { "noTestsAction": "error", diff --git a/cucumber_cpp/acceptance_test/steps/Steps.cpp b/cucumber_cpp/acceptance_test/steps/Steps.cpp index a6cc3119..49a1800f 100644 --- a/cucumber_cpp/acceptance_test/steps/Steps.cpp +++ b/cucumber_cpp/acceptance_test/steps/Steps.cpp @@ -1,4 +1,4 @@ -#include "cucumber_cpp/CucumberCpp.hpp" +#include "cucumber_cpp/Steps.hpp" #include "fmt/format.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/cucumber_cpp/acceptance_test/test.bats b/cucumber_cpp/acceptance_test/test.bats index e94a167a..66ebe93d 100644 --- a/cucumber_cpp/acceptance_test/test.bats +++ b/cucumber_cpp/acceptance_test/test.bats @@ -243,3 +243,13 @@ teardown() { refute_output --partial "HOOK_BEFORE_FEATURE" refute_output --partial "HOOK_AFTER_FEATURE" } + +@test "Successful asynchronous test" { + run $acceptance_test --format summary pretty message junit --parallel 2 --tags "@result:OK" -- cucumber_cpp/acceptance_test/features + assert_success +} + +@test "Failed asynchronous tests" { + run $acceptance_test --format summary pretty message junit --parallel 2 --tags "@smoke and @result:FAILED" -- cucumber_cpp/acceptance_test/features + assert_failure +} diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp index e86bb87c..8388357e 100644 --- a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.cpp @@ -125,12 +125,14 @@ namespace cucumber_cpp::library::runtime , options{ options } , supportCodeLibrary{ supportCodeLibrary } , programContext{ programContext } - , threadPool{ coro::thread_pool::make_unique(coro::thread_pool::options{ .thread_count = options.parallel }) } { } bool ParallelRuntimeAdapter::Run() { + std::unique_ptr workerThreadPool{ coro::thread_pool::make_unique(coro::thread_pool::options{ .thread_count = options.parallel }) }; + std::unique_ptr supportThreadPool{ coro::thread_pool::make_unique(coro::thread_pool::options{ .thread_count = 2 }) }; + std::vector> tasks; bool failing = false; runtime::Worker synchronousWorker{ testRunStartedId, broadcaster, idGenerator, options, supportCodeLibrary, programContext }; @@ -139,22 +141,22 @@ namespace cucumber_cpp::library::runtime if (!failing) { - ParallelBroadcaster parallelBroadcaster{ broadcaster, threadPool }; + ParallelBroadcaster parallelBroadcaster{ broadcaster, supportThreadPool }; tasks.emplace_back(parallelBroadcaster.BroadcastEventTask()); runtime::Worker parallelWorker{ testRunStartedId, parallelBroadcaster, idGenerator, options, supportCodeLibrary, programContext }; auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, idGenerator); - coro::latch taskLatch{ 10 * std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) -> const std::list& - { - return suite.testCases; - }) | - std::views::join) }; + coro::latch taskLatch{ std::ranges::distance(assembledTestSuites | std::views::transform([](const auto& suite) -> const std::list& + { + return suite.testCases; + }) | + std::views::join) }; for (const auto& assembledTestSuite : assembledTestSuites) for (const auto& assembledTestCase : assembledTestSuite.testCases) - tasks.emplace_back(RunTestCase(threadPool, taskLatch, parallelWorker, assembledTestSuite.gherkinDocument, assembledTestCase, programContext, failing)); + tasks.emplace_back(RunTestCase(workerThreadPool, taskLatch, parallelWorker, assembledTestSuite.gherkinDocument, assembledTestCase, programContext, failing)); tasks.emplace_back(parallelBroadcaster.ShutdownTask(taskLatch)); diff --git a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp index 5ffa1590..8bd63bde 100644 --- a/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp +++ b/cucumber_cpp/library/runtime/ParallelRuntimeAdapter.hpp @@ -6,12 +6,7 @@ #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" #include "cucumber_cpp/library/support/Types.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" -#include -#include -#include -#include #include -#include #include namespace cucumber_cpp::library::runtime @@ -36,8 +31,6 @@ namespace cucumber_cpp::library::runtime const support::RunOptions::Runtime& options; support::SupportCodeLibrary& supportCodeLibrary; Context& programContext; - - std::unique_ptr threadPool; }; } From 54f62ffc4aa956212a3783220761c832d7a5fbbd Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 13:08:38 +0000 Subject: [PATCH 07/20] fix: update cucumber_gherkin GIT_TAG to unreleased main and remove C++20 standard settings --- external/cucumber/gherkin/CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/external/cucumber/gherkin/CMakeLists.txt b/external/cucumber/gherkin/CMakeLists.txt index da3b0381..7519c30a 100644 --- a/external/cucumber/gherkin/CMakeLists.txt +++ b/external/cucumber/gherkin/CMakeLists.txt @@ -1,14 +1,9 @@ FetchContent_Declare( cucumber_gherkin GIT_REPOSITORY https://github.com/cucumber/gherkin.git - GIT_TAG 6fe0a3be9df9388209cca37bc3f254be10a6014e # v39.0.0 + GIT_TAG 5ec93f5ceef1d16cf1cc737ebd5eb35c127993ef # unreleased main past v39.0.0 ) FetchContent_MakeAvailable(cucumber_gherkin) add_subdirectory(${cucumber_gherkin_SOURCE_DIR}/cpp ${cucumber_gherkin_BINARY_DIR}/cpp) - -# The usage of designated initializers requires c++20 in MSVC -set_target_properties(cucumber_gherkin_lib PROPERTIES CXX_STANDARD 20) -set_target_properties(cucumber_gherkin_bin PROPERTIES CXX_STANDARD 20) -set_target_properties(cucumber_gherkin_generate_tokens_bin PROPERTIES CXX_STANDARD 20) From 52414ff7ceff5d08d7657dc5716c05e7c64023b1 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 13:12:21 +0000 Subject: [PATCH 08/20] feat: update CI configuration to support asynchronous build and test presets --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c03ba383..4ebc5e85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,8 +65,8 @@ jobs: max-size: 2G - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: - configurePreset: "Host" - buildPreset: "Host-Debug" + configurePreset: "Host-Asynchronous" + buildPreset: "Host-Asynchronous-Debug" - run: | bats --formatter junit cucumber_cpp/acceptance_test/test.bats | tee test-report.xml - uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0 From 26cbedb2af4168a06cf84d9081d9f48cd64f2117 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 13:51:42 +0000 Subject: [PATCH 09/20] fix: remove global compile option and conditionally set target compile options for libcoro --- external/jbaldwin/libcoro/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/external/jbaldwin/libcoro/CMakeLists.txt b/external/jbaldwin/libcoro/CMakeLists.txt index 3bc6c86f..84b673cc 100644 --- a/external/jbaldwin/libcoro/CMakeLists.txt +++ b/external/jbaldwin/libcoro/CMakeLists.txt @@ -1,4 +1,3 @@ -add_compile_options(-Wno-missing-field-initializers) FetchContent_Declare( libcoro @@ -13,4 +12,10 @@ set(LIBCORO_BUILD_EXAMPLES OFF CACHE STRING "") FetchContent_MakeAvailable(libcoro) -target_compile_options(libcoro INTERFACE $<$:-Wno-extra>) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + # libcoro sets public Wall and Wextra warnings, but we want to disable them for our project + target_compile_options(libcoro INTERFACE + $<$:-Wno-extra> + $<$:-Wno-missing-field-initializers> + ) +endif() From 4d126c1f1b4dfaa1f1894d8465e6cc9b006e1e95 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 13:58:52 +0000 Subject: [PATCH 10/20] feat: add cache variables for macOS Debug presets to specify Clang compiler paths --- CMakePresets.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index e60d3ad2..26ac5efa 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -64,7 +64,11 @@ { "name": "macos-Debug-synchronous", "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, synchronous", - "inherits": "host-single-Debug-synchronous" + "inherits": "host-single-Debug-synchronous", + "cacheVariables": { + "CMAKE_C_COMPILER": "$(brew --prefix llvm@20)/bin/clang", + "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@20)/bin/clang++" + } }, { "name": "host-single-Debug-asynchronous", @@ -90,7 +94,11 @@ { "name": "macos-Debug-asynchronous", "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, asynchronous", - "inherits": "host-single-Debug-asynchronous" + "inherits": "host-single-Debug-asynchronous", + "cacheVariables": { + "CMAKE_C_COMPILER": "$(brew --prefix llvm@20)/bin/clang", + "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@20)/bin/clang++" + } }, { "name": "host-single-time-profile", From dc80b55788a038b0ccb61cc5be2659ebd68a0c41 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 13:59:22 +0000 Subject: [PATCH 11/20] fix: update macOS Debug compiler paths to use LLVM 18 --- CMakePresets.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 26ac5efa..18d51b8b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -66,8 +66,8 @@ "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, synchronous", "inherits": "host-single-Debug-synchronous", "cacheVariables": { - "CMAKE_C_COMPILER": "$(brew --prefix llvm@20)/bin/clang", - "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@20)/bin/clang++" + "CMAKE_C_COMPILER": "$(brew --prefix llvm@18)/bin/clang", + "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@18)/bin/clang++" } }, { @@ -96,8 +96,8 @@ "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, asynchronous", "inherits": "host-single-Debug-asynchronous", "cacheVariables": { - "CMAKE_C_COMPILER": "$(brew --prefix llvm@20)/bin/clang", - "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@20)/bin/clang++" + "CMAKE_C_COMPILER": "$(brew --prefix llvm@18)/bin/clang", + "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@18)/bin/clang++" } }, { From 3af94f7ba05adfb2e79b5f6d97f13da59942f315 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 14:04:24 +0000 Subject: [PATCH 12/20] refactor: simplify macOS Debug presets by removing redundant cache variables --- CMakePresets.json | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 18d51b8b..e60d3ad2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -64,11 +64,7 @@ { "name": "macos-Debug-synchronous", "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, synchronous", - "inherits": "host-single-Debug-synchronous", - "cacheVariables": { - "CMAKE_C_COMPILER": "$(brew --prefix llvm@18)/bin/clang", - "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@18)/bin/clang++" - } + "inherits": "host-single-Debug-synchronous" }, { "name": "host-single-Debug-asynchronous", @@ -94,11 +90,7 @@ { "name": "macos-Debug-asynchronous", "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, asynchronous", - "inherits": "host-single-Debug-asynchronous", - "cacheVariables": { - "CMAKE_C_COMPILER": "$(brew --prefix llvm@18)/bin/clang", - "CMAKE_CXX_COMPILER": "$(brew --prefix llvm@18)/bin/clang++" - } + "inherits": "host-single-Debug-asynchronous" }, { "name": "host-single-time-profile", From 88e5a827c1917a60fef18f17b41533267c3431a3 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 14:07:35 +0000 Subject: [PATCH 13/20] refactor: replace floating-point literals with integer constants for time conversions --- cucumber_cpp/library/util/Timestamp.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cucumber_cpp/library/util/Timestamp.hpp b/cucumber_cpp/library/util/Timestamp.hpp index a4ab865a..ffc20cc4 100644 --- a/cucumber_cpp/library/util/Timestamp.hpp +++ b/cucumber_cpp/library/util/Timestamp.hpp @@ -4,14 +4,13 @@ #include "cucumber/messages/duration.hpp" #include "cucumber/messages/timestamp.hpp" #include -#include #include namespace cucumber_cpp::library::util { - constexpr std::size_t millisecondsPerSecond = 1e3; - constexpr std::size_t nanosecondsPerMillisecond = 1e6; - constexpr std::size_t nanosecondsPerSecond = 1e9; + constexpr auto millisecondsPerSecond{ 1000u }; + constexpr auto nanosecondsPerMillisecond{ 1000000u }; + constexpr auto nanosecondsPerSecond{ 1000000000u }; struct TimestampGenerator { From 2e7d140d73541b0704ceac974aebdbc4162b61a7 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 14:16:48 +0000 Subject: [PATCH 14/20] refactor: change floating-point literals to std::size_t for time constants --- cucumber_cpp/library/util/Timestamp.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cucumber_cpp/library/util/Timestamp.hpp b/cucumber_cpp/library/util/Timestamp.hpp index ffc20cc4..8a483095 100644 --- a/cucumber_cpp/library/util/Timestamp.hpp +++ b/cucumber_cpp/library/util/Timestamp.hpp @@ -4,13 +4,14 @@ #include "cucumber/messages/duration.hpp" #include "cucumber/messages/timestamp.hpp" #include +#include #include namespace cucumber_cpp::library::util { - constexpr auto millisecondsPerSecond{ 1000u }; - constexpr auto nanosecondsPerMillisecond{ 1000000u }; - constexpr auto nanosecondsPerSecond{ 1000000000u }; + constexpr std::size_t millisecondsPerSecond{ 1000u }; + constexpr std::size_t nanosecondsPerMillisecond{ 1000000u }; + constexpr std::size_t nanosecondsPerSecond{ 1000000000u }; struct TimestampGenerator { From 66b8724235ef1610e0db6ae77e8f57875143fc3a Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 14:24:45 +0000 Subject: [PATCH 15/20] refactor: rename parameters in Step methods for clarity --- cucumber_cpp/library/engine/Step.cpp | 12 ++++++------ cucumber_cpp/library/engine/Step.hpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cucumber_cpp/library/engine/Step.cpp b/cucumber_cpp/library/engine/Step.cpp index 551b447c..0aa60ec5 100644 --- a/cucumber_cpp/library/engine/Step.cpp +++ b/cucumber_cpp/library/engine/Step.cpp @@ -25,18 +25,18 @@ namespace cucumber_cpp::library::engine nestedTestCaseRunner.Step(step); } - void StepBase::Step(const std::string& step, const std::optional& docString) const + void StepBase::Step(const std::string& step, const std::optional& nestedDocString) const { - nestedTestCaseRunner.Step(step, util::TransformDocString(docString)); + nestedTestCaseRunner.Step(step, util::TransformDocString(nestedDocString)); } - void StepBase::Step(const std::string& step, const std::optional& dataTable) const + void StepBase::Step(const std::string& step, const std::optional& nestedDataTable) const { - nestedTestCaseRunner.Step(step, util::TransformTable(dataTable)); + nestedTestCaseRunner.Step(step, util::TransformTable(nestedDataTable)); } - void StepBase::Step(const std::string& step, const std::optional& dataTable, const std::optional& docString) const + void StepBase::Step(const std::string& step, const std::optional& nestedDataTable, const std::optional& nestedDocString) const { - nestedTestCaseRunner.Step(step, util::TransformTable(dataTable), util::TransformDocString(docString)); + nestedTestCaseRunner.Step(step, util::TransformTable(nestedDataTable), util::TransformDocString(nestedDocString)); } } diff --git a/cucumber_cpp/library/engine/Step.hpp b/cucumber_cpp/library/engine/Step.hpp index 63d449d1..05b9c84a 100644 --- a/cucumber_cpp/library/engine/Step.hpp +++ b/cucumber_cpp/library/engine/Step.hpp @@ -41,9 +41,9 @@ namespace cucumber_cpp::library::engine protected: void Step(const std::string& step) const; - void Step(const std::string& step, const std::optional& docString) const; - void Step(const std::string& step, const std::optional& dataTable) const; - void Step(const std::string& step, const std::optional& dataTable, const std::optional& docString) const; + void Step(const std::string& step, const std::optional& nestedDocString) const; + void Step(const std::string& step, const std::optional& nestedDataTable) const; + void Step(const std::string& step, const std::optional& nestedDataTable, const std::optional& nestedDocString) const; const runtime::NestedTestCaseRunner& nestedTestCaseRunner; From a4e3f7415f03544938d8066580df100c99017e82 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 15:50:24 +0000 Subject: [PATCH 16/20] fix: update cucumber_gherkin GIT_TAG to match unrelease PR --- external/cucumber/gherkin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/cucumber/gherkin/CMakeLists.txt b/external/cucumber/gherkin/CMakeLists.txt index 7519c30a..9c832ce5 100644 --- a/external/cucumber/gherkin/CMakeLists.txt +++ b/external/cucumber/gherkin/CMakeLists.txt @@ -1,7 +1,7 @@ FetchContent_Declare( cucumber_gherkin GIT_REPOSITORY https://github.com/cucumber/gherkin.git - GIT_TAG 5ec93f5ceef1d16cf1cc737ebd5eb35c127993ef # unreleased main past v39.0.0 + GIT_TAG 5e4222e3956d52c8758f41727b14a1ab2dce5060 # unrelease PR ) FetchContent_MakeAvailable(cucumber_gherkin) From a65fee85acaf86df7b3ecca73a1217f11c7790dc Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 16:04:15 +0000 Subject: [PATCH 17/20] refactor: remove unused runPassed member from Application class --- cucumber_cpp/library/Application.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/cucumber_cpp/library/Application.hpp b/cucumber_cpp/library/Application.hpp index 01e7dc1e..6807629f 100644 --- a/cucumber_cpp/library/Application.hpp +++ b/cucumber_cpp/library/Application.hpp @@ -89,8 +89,6 @@ namespace cucumber_cpp::library bool removeDefaultGoogleTestListener; util::StopWatchHighResolutionClock stopwatchHighResolutionClock; util::TimestampGeneratorSystemClock timestampGeneratorSystemClock; - - bool runPassed{ false }; }; } From dcda94674296087680d5a9a03e169582b84f268f Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Fri, 20 Mar 2026 16:32:26 +0000 Subject: [PATCH 18/20] refactor: mark parameters as maybe unused in step definitions --- compatibility/ambiguous/ambiguous.cpp | 4 ++-- compatibility/minimal/minimal.cpp | 2 +- compatibility/regular-expression/regular-expression.cpp | 2 +- compatibility/rules-backgrounds/rules-backgrounds.cpp | 2 +- .../unknown-parameter-type/unknown-parameter-type.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compatibility/ambiguous/ambiguous.cpp b/compatibility/ambiguous/ambiguous.cpp index 0a21f56b..f9eaec81 100644 --- a/compatibility/ambiguous/ambiguous.cpp +++ b/compatibility/ambiguous/ambiguous.cpp @@ -1,12 +1,12 @@ #include "cucumber_cpp/Steps.hpp" #include -STEP(R"(^a (.*?) with (.*?)$)", (const std::string& arg1, const std::string& arg2)) +STEP(R"(^a (.*?) with (.*?)$)", ([[maybe_unused]] const std::string& arg1, [[maybe_unused]] const std::string& arg2)) { // no-op } -STEP(R"(^a step with (.*?)$)", (const std::string& arg1)) +STEP(R"(^a step with (.*?)$)", ([[maybe_unused]] const std::string& arg1)) { // no-op } diff --git a/compatibility/minimal/minimal.cpp b/compatibility/minimal/minimal.cpp index c0a8a2a6..2b33833c 100644 --- a/compatibility/minimal/minimal.cpp +++ b/compatibility/minimal/minimal.cpp @@ -1,7 +1,7 @@ #include "cucumber_cpp/Steps.hpp" #include -STEP(R"(I have {int} cukes in my belly)", (std::int32_t number)) +STEP(R"(I have {int} cukes in my belly)", ([[maybe_unused]] std::int32_t number)) { // no-op } diff --git a/compatibility/regular-expression/regular-expression.cpp b/compatibility/regular-expression/regular-expression.cpp index b626266d..0ac85382 100644 --- a/compatibility/regular-expression/regular-expression.cpp +++ b/compatibility/regular-expression/regular-expression.cpp @@ -2,7 +2,7 @@ #include #include -STEP(R"(^a (.*?)(?: and a (.*?))?(?: and a (.*?))?$)", (const std::optional& vegetable1, const std::optional& vegetable2, const std::optional& vegetable3)) +STEP(R"(^a (.*?)(?: and a (.*?))?(?: and a (.*?))?$)", ([[maybe_unused]] const std::optional& vegetable1, [[maybe_unused]] const std::optional& vegetable2, [[maybe_unused]] const std::optional& vegetable3)) { // no-op } diff --git a/compatibility/rules-backgrounds/rules-backgrounds.cpp b/compatibility/rules-backgrounds/rules-backgrounds.cpp index 2df02212..d9bee7df 100644 --- a/compatibility/rules-backgrounds/rules-backgrounds.cpp +++ b/compatibility/rules-backgrounds/rules-backgrounds.cpp @@ -1,7 +1,7 @@ #include "cucumber_cpp/Steps.hpp" #include -GIVEN(R"(an order for {string})", (const std::string& order)) +GIVEN(R"(an order for {string})", ([[maybe_unused]] const std::string& order)) { // no-op } diff --git a/compatibility/unknown-parameter-type/unknown-parameter-type.cpp b/compatibility/unknown-parameter-type/unknown-parameter-type.cpp index 34fda945..ce6d1ada 100644 --- a/compatibility/unknown-parameter-type/unknown-parameter-type.cpp +++ b/compatibility/unknown-parameter-type/unknown-parameter-type.cpp @@ -4,7 +4,7 @@ struct Airport {}; -GIVEN(R"({airport} is closed because of a strike)", (const Airport& airport)) +GIVEN(R"({airport} is closed because of a strike)", ([[maybe_unused]] const Airport& airport)) { FAIL() << "Should not be called because airport parameter type has not been defined"; } From a4987dc8cc1c62c3664a1add7e51d4f4caa9be98 Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Mon, 23 Mar 2026 08:37:33 +0000 Subject: [PATCH 19/20] fix: update cucumber_gherkin GIT_TAG to match unreleased main --- external/cucumber/gherkin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/cucumber/gherkin/CMakeLists.txt b/external/cucumber/gherkin/CMakeLists.txt index 9c832ce5..e415cc10 100644 --- a/external/cucumber/gherkin/CMakeLists.txt +++ b/external/cucumber/gherkin/CMakeLists.txt @@ -1,7 +1,7 @@ FetchContent_Declare( cucumber_gherkin GIT_REPOSITORY https://github.com/cucumber/gherkin.git - GIT_TAG 5e4222e3956d52c8758f41727b14a1ab2dce5060 # unrelease PR + GIT_TAG 9f99514f288381e0f614cfb74c856710f1584574 # unreleased main ) FetchContent_MakeAvailable(cucumber_gherkin) From 963455f1330991b415d3b252fdc4a9a786c9754c Mon Sep 17 00:00:00 2001 From: "Timmer, Daan" Date: Sun, 29 Mar 2026 20:53:06 +0000 Subject: [PATCH 20/20] chore: update libcoro to forked version fixing Wall and Wextra being publicly set --- external/cucumber/messages/CMakeLists.txt | 5 ----- external/jbaldwin/libcoro/CMakeLists.txt | 13 +++---------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/external/cucumber/messages/CMakeLists.txt b/external/cucumber/messages/CMakeLists.txt index 8d6f34ef..7122f3b8 100644 --- a/external/cucumber/messages/CMakeLists.txt +++ b/external/cucumber/messages/CMakeLists.txt @@ -8,8 +8,3 @@ FetchContent_Declare( FetchContent_MakeAvailable(cucumber_messages) add_subdirectory(${cucumber_messages_SOURCE_DIR}/cpp ${cucumber_messages_BINARY_DIR}/cpp) - -target_compile_options(cucumber_messages_lib PUBLIC - # cucumber_gherkin-src/cpp/include/gherkin/cucumber/gherkin/utils.hpp:34:31: error: 'codecvt_utf8' is deprecated - $<$:-Wno-deprecated-declarations> -) diff --git a/external/jbaldwin/libcoro/CMakeLists.txt b/external/jbaldwin/libcoro/CMakeLists.txt index 84b673cc..45a91ad7 100644 --- a/external/jbaldwin/libcoro/CMakeLists.txt +++ b/external/jbaldwin/libcoro/CMakeLists.txt @@ -1,8 +1,9 @@ FetchContent_Declare( libcoro - GIT_REPOSITORY https://github.com/jbaldwin/libcoro.git - GIT_TAG 0161911f260a7507ac1dd4aa26da2e2c092fb550 # v0.16.0 + # GIT_REPOSITORY https://github.com/jbaldwin/libcoro.git + GIT_REPOSITORY https://github.com/daantimmer/libcoro.git + GIT_TAG 9b2182cedbb95a563025b1a06d38b3989097c5d6 # public contribution on forked repository fixing warnings SYSTEM ) @@ -11,11 +12,3 @@ set(LIBCORO_BUILD_TESTS OFF CACHE STRING "") set(LIBCORO_BUILD_EXAMPLES OFF CACHE STRING "") FetchContent_MakeAvailable(libcoro) - -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") - # libcoro sets public Wall and Wextra warnings, but we want to disable them for our project - target_compile_options(libcoro INTERFACE - $<$:-Wno-extra> - $<$:-Wno-missing-field-initializers> - ) -endif()