diff --git a/.clang-tidy b/.clang-tidy index 83b2c607..273dcac3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -15,6 +15,7 @@ Checks: ' -misc-non-private-member-variables-in-classes, modernize*, -modernize-use-trailing-return-type, + -modernize-use-designated-initializers, performance*, -performance-no-int-to-ptr, readability*, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16807f35..79029c95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,18 @@ concurrency: env: GTEST_COLOR: 1 CPM_SOURCE_CACHE: ${{ github.workspace }}/.cpm + # renovate: datasource=git-refs depName=cucumber/messages packageName=https://github.com/cucumber/messages currentValue=main + MESSAGES_REF: main + # renovate: datasource=git-refs depName=cucumber/gherkin packageName=https://github.com/cucumber/gherkin currentValue=main + GHERKIN_REF: main + # renovate: datasource=git-refs depName=cucumber/query packageName=https://github.com/cucumber/query currentValue=feature/cpp + QUERY_REF: feature/cpp + # renovate: datasource=git-refs depName=cucumber/tag-expressions packageName=https://github.com/cucumber/tag-expressions currentValue=feature/cpp + TAG_EXPRESSIONS_REF: feature/cpp + # renovate: datasource=git-refs depName=cucumber/pretty-formatter packageName=https://github.com/cucumber/pretty-formatter currentValue=feature/cpp + PRETTY_FORMATTER_REF: feature/cpp + # renovate: datasource=git-refs depName=cucumber/cucumber-expressions packageName=https://github.com/cucumber/cucumber-expressions currentValue=feature/cpp + CUCUMBER_EXPRESSIONS_REF: feature/cpp jobs: build-in-devcontainer: @@ -120,3 +132,213 @@ jobs: with: name: test-logs path: .build/${{ matrix.os }}-Debug-${{ matrix.type }}${{ matrix.preset_suffix }}/Testing/Temporary/ + + test-cpp-system-packages-linux: + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - { os: ubuntu-24.04, cxx: g++, version: 12 } + - { os: ubuntu-24.04, cxx: g++, version: 13 } + - { os: ubuntu-24.04, cxx: clang++, version: 16 } + - { os: ubuntu-24.04, cxx: clang++, version: 17 } + - { os: ubuntu-26.04, cxx: g++, version: 14 } + - { os: ubuntu-26.04, cxx: g++, version: 15 } + - { os: ubuntu-26.04, cxx: clang++, version: 21 } + - { os: ubuntu-26.04, cxx: clang++, version: 22 } + env: + CXX: ${{ matrix.config.cxx }}-${{ matrix.config.version }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + submodules: true + + - name: ccache + uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23 + with: + key: ${{ github.job }}-${{ matrix.config.os }}-${{ matrix.config.cxx }}-${{ matrix.config.version }}-${{ github.ref }} + append-timestamp: false + save: ${{ github.ref == 'refs/heads/main' }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y nlohmann-json3-dev libgtest-dev libgmock-dev libabsl-dev libre2-dev libcli11-dev libpugixml-dev + + # Ubuntu 24.04 ships fmt 9; newer runners provide fmt 10+ via apt. + - name: Install libfmt (apt) + if: matrix.config.os != 'ubuntu-24.04' + run: sudo apt-get install -y libfmt-dev + + - name: Checkout libfmt + if: matrix.config.os == 'ubuntu-24.04' + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: fmtlib/fmt + ref: 10.2.1 + path: fmt-src + persist-credentials: false + + - name: Install libfmt (from source) + if: matrix.config.os == 'ubuntu-24.04' + run: | + cmake -S fmt-src -B fmt-src/build \ + -DCMAKE_BUILD_TYPE=Release \ + -DFMT_TEST=OFF -DFMT_DOC=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + cmake --build fmt-src/build + sudo cmake --install fmt-src/build + + - name: Install cucumber/tag-expressions + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/tag-expressions + ref: ${{ env.TAG_EXPRESSIONS_REF }} + + - name: Install cucumber/cucumber-expressions + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/cucumber-expressions + ref: ${{ env.CUCUMBER_EXPRESSIONS_REF }} + + - name: Install cucumber/messages + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/messages + ref: ${{ env.MESSAGES_REF }} + + - name: Install cucumber/gherkin + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/gherkin + ref: ${{ env.GHERKIN_REF }} + + - name: Install cucumber/query + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/query + ref: ${{ env.QUERY_REF }} + + - name: Install cucumber/pretty-formatter + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/pretty-formatter + ref: ${{ env.PRETTY_FORMATTER_REF }} + + - name: Build and unit test + run: cmake --workflow --preset test-system + + test-cpp-system-packages-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + submodules: true + + - name: ccache + uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23 + with: + key: ${{ github.job }}-${{ github.ref }} + append-timestamp: false + save: ${{ github.ref == 'refs/heads/main' }} + + - name: Install dependencies + run: brew install googletest nlohmann-json fmt abseil re2 cli11 pugixml + + - name: Install cucumber/tag-expressions + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/tag-expressions + ref: ${{ env.TAG_EXPRESSIONS_REF }} + + - name: Install cucumber/cucumber-expressions + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/cucumber-expressions + ref: ${{ env.CUCUMBER_EXPRESSIONS_REF }} + + - name: Install cucumber/messages + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/messages + ref: ${{ env.MESSAGES_REF }} + + - name: Install cucumber/gherkin + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/gherkin + ref: ${{ env.GHERKIN_REF }} + + - name: Install cucumber/query + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/query + ref: ${{ env.QUERY_REF }} + + - name: Install cucumber/pretty-formatter + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/pretty-formatter + ref: ${{ env.PRETTY_FORMATTER_REF }} + + - name: Build and unit test + run: cmake --workflow --preset test-system + + test-cpp-system-packages-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + submodules: true + + - name: Install build tools + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 + + - name: Install dependencies + shell: pwsh + run: | + vcpkg install gtest nlohmann-json fmt abseil re2 cli11 pugixml --triplet x64-windows + # Expose the vcpkg toolchain so find_package() locates the packages. + echo "CMAKE_TOOLCHAIN_FILE=$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> $env:GITHUB_ENV + + - name: Install cucumber/tag-expressions + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/tag-expressions + ref: ${{ env.TAG_EXPRESSIONS_REF }} + + - name: Install cucumber/cucumber-expressions + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/cucumber-expressions + ref: ${{ env.CUCUMBER_EXPRESSIONS_REF }} + + - name: Install cucumber/messages + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/messages + ref: ${{ env.MESSAGES_REF }} + + - name: Install cucumber/gherkin + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/gherkin + ref: ${{ env.GHERKIN_REF }} + + - name: Install cucumber/query + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/query + ref: ${{ env.QUERY_REF }} + + - name: Install cucumber/pretty-formatter + uses: cucumber/ci-actions/install-cpp-dep@9ba4a623ec838b1358e18683a85a0914f3779363 + with: + repository: cucumber/pretty-formatter + ref: ${{ env.PRETTY_FORMATTER_REF }} + - name: Build and unit test + run: cmake --workflow --preset test-system diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d1d2ddf..e5c259a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,12 +19,23 @@ endif() option(CCR_FETCH_DEPS "Fetch dependencies via FetchContent." ${CCR_DEFAULTOPT} ) option(CCR_BUILD_TESTS "Enable build of the tests" ${CCR_DEFAULTOPT}) -option(CCR_USE_RE2 "Use the RE2 regex engine (fetched when CCR_FETCH_DEPS is set, otherwise auto-detected)" ${CCR_DEFAULTOPT}) + option(CCR_ENABLE_COVERAGE "Enable compiler flags for code coverage measurements" Off) +option(CCR_ENABLE_SANITIZERS "Enable compiler flags for the address and undefined behaviour sanitizers" Off) option(CCR_ENABLE_TIME_PROFILE "Enable compiler flags for time profiling measurements" Off) +option(CCR_USE_RE2 "Use the RE2 regex engine (fetched when CCR_FETCH_DEPS is set, otherwise auto-detected)" ${CCR_DEFAULTOPT}) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +if(CCR_ENABLE_SANITIZERS AND CCR_ENABLE_COVERAGE) + message(FATAL_ERROR "CCR_ENABLE_SANITIZERS and CCR_ENABLE_COVERAGE are mutually exclusive.") +endif() + +if(CCR_ENABLE_COVERAGE AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(WARNING "Code coverage is only enabled with the GNU compiler.") +endif() + if (CCR_BUILD_TESTS) ccr_enable_testing() endif() @@ -80,64 +91,28 @@ include(FetchContent) include(GNUInstallDirs) include(CTest) -# Enable sanitizers globally, before any dependency is added, so every target -# (including fetched third-party libraries) is instrumented. Consistent -# instrumentation keeps libc++ container annotations coherent across module -# boundaries; mixing instrumented and uninstrumented code produces false-positive -# container-overflow reports. -if (PROJECT_IS_TOP_LEVEL AND NOT CCR_ENABLE_COVERAGE - AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR CMAKE_CXX_SIMULATE_ID MATCHES "MSVC")) - set(CCR_ENABLE_SANITIZERS On) -endif() +include(cmake/dependencies.cpm.cmake) +add_subdirectory(external) if (CCR_ENABLE_SANITIZERS) - add_compile_options(-fsanitize=address -fsanitize=undefined) - add_link_options(-fsanitize=address -fsanitize=undefined) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-fsanitize=address -fsanitize=undefined) + add_link_options(-fsanitize=address -fsanitize=undefined) + endif() endif() -include(cmake/dependencies.cpm.cmake) -add_subdirectory(external) - if (TARGET re2::re2) set(CCR_HAS_RE2 On) else() set(CCR_HAS_RE2 Off) endif() -# Opt specific dependencies out of sanitizer instrumentation because of known -# limitations: abseil's constexpr function-pointer comparisons are not compatible -# with GCC's ASan/UBSan instrumentation, which causes build failures. Later target -# options override the globally inherited flags. -if (CCR_ENABLE_SANITIZERS) - function(ccr_disable_sanitizers dir) - get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS) - foreach(target IN LISTS targets) - get_target_property(type ${target} TYPE) - if (NOT type STREQUAL "INTERFACE_LIBRARY" AND NOT type STREQUAL "UTILITY") - target_compile_options(${target} PRIVATE -fno-sanitize=address -fno-sanitize=undefined) - target_link_options(${target} PRIVATE -fno-sanitize=address -fno-sanitize=undefined) - endif() - endforeach() - - get_property(subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES) - foreach(subdir IN LISTS subdirs) - ccr_disable_sanitizers("${subdir}") - endforeach() - endfunction() - - foreach(dep_dir IN ITEMS "${abseil-cpp_SOURCE_DIR}" "${re2_SOURCE_DIR}") - if (dep_dir AND IS_DIRECTORY "${dep_dir}") - ccr_disable_sanitizers("${dep_dir}") - endif() - endforeach() -endif() - if(PROJECT_IS_TOP_LEVEL) set(CMAKE_COMPILE_WARNING_AS_ERROR On) endif() add_subdirectory(cucumber_cpp) -if (PROJECT_IS_TOP_LEVEL) +if (CCR_BUILD_TESTS) add_subdirectory(compatibility) endif() diff --git a/CMakePresets.json b/CMakePresets.json index ea3d2ed6..4f3e1a53 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -16,12 +16,26 @@ "CMAKE_CONFIGURATION_TYPES": "Debug;RelWithDebInfo;Release" } }, + { + "name": "test-system", + "displayName": "Test build (system dependencies)", + "description": "Build with unit tests and sanitizers enabled using system dependencies. Used by CI to build and run the test suite.", + "inherits": "defaults", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CCR_FETCH_DEPS": "Off", + "CCR_BUILD_TESTS": "On", + "CCR_ENABLE_SANITIZERS": "On" + } + }, { "name": "Coverage", "inherits": "defaults", "generator": "Ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", + "CCR_BUILD_TESTS": "On", "CCR_ENABLE_COVERAGE": "On" } }, @@ -36,6 +50,7 @@ "inherits": "defaults", "generator": "Ninja", "cacheVariables": { + "CCR_FETCH_DEPS": "On", "CMAKE_BUILD_TYPE": "Debug" } }, @@ -112,6 +127,10 @@ } ], "buildPresets": [ + { + "name": "test-system", + "configurePreset": "test-system" + }, { "name": "Coverage", "configurePreset": "Coverage" @@ -195,6 +214,12 @@ "stopOnFailure": false } }, + { + "name": "test-system", + "configurePreset": "test-system", + "configuration": "Debug", + "inherits": "defaults" + }, { "name": "Coverage", "configurePreset": "Coverage", @@ -251,6 +276,23 @@ } ], "workflowPresets": [ + { + "name": "test-system", + "steps": [ + { + "type": "configure", + "name": "test-system" + }, + { + "type": "build", + "name": "test-system" + }, + { + "type": "test", + "name": "test-system" + } + ] + }, { "name": "ubuntu-Debug-synchronous", "steps": [ diff --git a/cmake/dependencies.cpm.cmake b/cmake/dependencies.cpm.cmake index 52d3f693..df450ef3 100644 --- a/cmake/dependencies.cpm.cmake +++ b/cmake/dependencies.cpm.cmake @@ -5,32 +5,35 @@ macro(ccr_dependency PREFIX TAG DIGEST) endmacro() # renovate: datasource=github-tags packageName=cpm-cmake/CPM.cmake versioning=semver -ccr_dependency(CPM_DOWNLOAD v0.43.1 456cb6754daaa010d57444d0c8ce6d95ecf006ab) +ccr_dependency(CPM_DOWNLOAD v0.43.1 456cb6754daaa010d57444d0c8ce6d95ecf006ab) set(CPM_DOWNLOAD_SHA256 "1c40fc102ce9625d7de7eb14f541cab30cc3138dca627f0b0ec40293ce6c2934") # renovate: datasource=github-tags packageName=nlohmann/json versioning=semver -ccr_dependency(NLOHMANN_JSON v3.12.0 55f93686c01528224f448c19128836e7df245f72) -# renovate: datasource=github-tags packageName=cucumber/messages versioning=semver -ccr_dependency(CUCUMBER_MESSAGES v34.0.0 8cd6cacecd0a657095c93b069c850f51009e0499) -# renovate: datasource=github-tags packageName=cucumber/gherkin versioning=semver -ccr_dependency(CUCUMBER_GHERKIN v42.0.0 85b41deb147e18421fe1023a0c496558caccaaa9) -# renovate: datasource=github-tags packageName=cucumber/query versioning=semver -ccr_dependency(CUCUMBER_QUERY v1.0.0 2d432352bb6d82c4092ce8b33f280149b1f7fcea) -# renovate: datasource=github-tags packageName=cucumber/tag-expressions versioning=semver -ccr_dependency(CUCUMBER_TAG_EXPRESSIONS v1.0.0 2d2e9ac1d84a2e05154fa982a276a04e3d1b3650) +ccr_dependency(NLOHMANN_JSON v3.12.0 55f93686c01528224f448c19128836e7df245f72) # renovate: datasource=github-tags packageName=google/googletest versioning=semver -ccr_dependency(GOOGLE_TEST v1.17.0 52eb8108c5bdec04579160ae17225d66034bd723) +ccr_dependency(GOOGLE_TEST v1.17.0 52eb8108c5bdec04579160ae17225d66034bd723) # renovate: datasource=github-tags packageName=CLIUtils/CLI11 versioning=semver -ccr_dependency(CLI11 v2.6.2 37bb6edc5317e99af72ef48405e65d9ca5218861) +ccr_dependency(CLI11 v2.6.2 37bb6edc5317e99af72ef48405e65d9ca5218861) # renovate: datasource=github-tags packageName=fmtlib/fmt versioning=semver -ccr_dependency(LIBFMT v12.1.0 407c905e45ad75fc29bf0f9bb7c5c2fd3475976f) +ccr_dependency(LIBFMT v12.1.0 407c905e45ad75fc29bf0f9bb7c5c2fd3475976f) # renovate: datasource=github-tags packageName=zeux/pugixml versioning=semver -ccr_dependency(PUGIXML v1.15 ee86beb30e4973f5feffe3ce63bfa4fbadf72f38) +ccr_dependency(PUGIXML v1.15 ee86beb30e4973f5feffe3ce63bfa4fbadf72f38) # renovate: datasource=github-tags packageName=abseil/abseil-cpp -ccr_dependency(ABSEIL_CPP 20250814.2 0cf0a5c9d12cc3783363ab20f11613e69fd04c9a) +ccr_dependency(ABSEIL_CPP 20250814.2 0cf0a5c9d12cc3783363ab20f11613e69fd04c9a) # renovate: datasource=github-tags packageName=google/re2 -ccr_dependency(RE2 2025-08-12 0f6c07eae69151e606acb3d9232750c3442dff23) -# renovate: datasource=github-tags packageName=jbeder/yaml-cpp versioning=semver -ccr_dependency(YAML_CPP v0.9.0 56e3bb550c91fd7005566f19c079cb7a503223cf) +ccr_dependency(RE2 2025-08-12 0f6c07eae69151e606acb3d9232750c3442dff23) + +# renovate: datasource=github-tags packageName=cucumber/messages versioning=semver +ccr_dependency(CUCUMBER_MESSAGES v34.0.0 23012bdd487ee74b84a267a9c6d60a9ab4822739) +# renovate: datasource=github-tags packageName=cucumber/gherkin versioning=semver +ccr_dependency(CUCUMBER_GHERKIN v42.0.0 8e98d323a61ba8c4c651d73f83f5dc68caf1d653) +# renovate: datasource=github-tags packageName=cucumber/query versioning=semver +ccr_dependency(CUCUMBER_QUERY v1.0.0 3dca317fdab2b16ae837dae7affab4c2e4120adf) +# renovate: datasource=github-tags packageName=cucumber/tag-expressions versioning=semver +ccr_dependency(CUCUMBER_TAG_EXPRESSIONS v1.0.0 2a7ff400647e3653863a9d3d7a1a871e708e85e0) +# renovate: datasource=github-tags packageName=cucumber/pretty-formatter versioning=semver +ccr_dependency(CUCUMBER_PRETTY_FORMATTER v1.0.0 3214db859fb3c99c0082be07465bb427b90923ee) +# renovate: datasource=github-tags packageName=cucumber/cucumber-expressions versioning=semver +ccr_dependency(CUCUMBER_CUCUMBER_EXPRESSIONS v1.0.0 15d8d40403fced47a92a2acbe4865bad0b924e3d) if(CCR_FETCH_DEPS) if(NOT COMMAND CPMAddPackage) @@ -67,13 +70,95 @@ if(CCR_FETCH_DEPS) # --------------------------------------------------------------------------- # nlohmann_json # --------------------------------------------------------------------------- - CPMAddPackage( - URI "gh:nlohmann/json@${NLOHMANN_JSON_VERSION}#${NLOHMANN_JSON_DIGEST}" - NAME nlohmann_json - OPTIONS - "JSON_BuildTests Off" - "JSON_Install ON" - ) + if(NOT TARGET nlohmann_json::nlohmann_json) + CPMAddPackage( + URI "gh:nlohmann/json@${NLOHMANN_JSON_VERSION}#${NLOHMANN_JSON_DIGEST}" + NAME nlohmann_json + OPTIONS + "JSON_BuildTests Off" + "JSON_Install ON" + ) + endif() + + # --------------------------------------------------------------------------- + # GoogleTest + # --------------------------------------------------------------------------- + if(NOT TARGET GTest::gtest) + CPMAddPackage( + URI "gh:google/googletest@${GOOGLE_TEST_VERSION}#${GOOGLE_TEST_DIGEST}" + NAME googletest + OPTIONS + "INSTALL_GTEST OFF" + "gtest_force_shared_crt ON" + ) + endif() + + if (TARGET GTest::gtest) + set_target_properties(gtest gtest_main gmock gmock_main PROPERTIES + FOLDER External/GoogleTest + ) + target_compile_options(gtest PRIVATE $<$:-Wno-character-conversion>) + target_compile_options(gmock PRIVATE $<$:-Wno-character-conversion>) + endif() + + # --------------------------------------------------------------------------- + # cli11 + # --------------------------------------------------------------------------- + if(NOT TARGET CLI11::CLI11) + CPMAddPackage( + URI "gh:CLIUtils/CLI11@${CLI11_VERSION}#${CLI11_DIGEST}" + NAME cli11 + ) + endif() + + # --------------------------------------------------------------------------- + # libfmt + # --------------------------------------------------------------------------- + if(NOT TARGET fmt::fmt) + CPMAddPackage( + URI "gh:fmtlib/fmt@${LIBFMT_VERSION}#${LIBFMT_DIGEST}" + NAME fmt + OPTIONS + "FMT_INSTALL ON" + ) + endif() + + # --------------------------------------------------------------------------- + # pugixml + # --------------------------------------------------------------------------- + if(NOT TARGET pugixml::pugixml) + CPMAddPackage( + URI "gh:zeux/pugixml@${PUGIXML_VERSION}#${PUGIXML_DIGEST}" + NAME pugixml + ) + endif() + + if (CCR_USE_RE2) + # --------------------------------------------------------------------------- + # abseil-cpp + # --------------------------------------------------------------------------- + if(NOT TARGET absl::base) + CPMAddPackage( + URI "gh:abseil/abseil-cpp@${ABSEIL_CPP_VERSION}#${ABSEIL_CPP_DIGEST}" + NAME abseil-cpp + OPTIONS + "ABSL_PROPAGATE_CXX_STD ON" + "ABSL_ENABLE_INSTALL ON" + ) + endif() + + # --------------------------------------------------------------------------- + # re2 + # --------------------------------------------------------------------------- + if(NOT TARGET re2::re2) + CPMAddPackage( + URI "gh:google/re2#${RE2_DIGEST}" + NAME re2 + OPTIONS + "RE2_BUILD_TESTING OFF" + ) + endif() + endif() # --------------------------------------------------------------------------- # cucumber_messages @@ -112,99 +197,33 @@ if(CCR_FETCH_DEPS) ) # --------------------------------------------------------------------------- - # GoogleTest + # cucumber_pretty_formatter # --------------------------------------------------------------------------- CPMAddPackage( - URI "gh:google/googletest@${GOOGLE_TEST_VERSION}#${GOOGLE_TEST_DIGEST}" - NAME googletest - OPTIONS - "INSTALL_GTEST OFF" - "gtest_force_shared_crt ON" - ) - - if (TARGET gtest) - set_target_properties(gtest gtest_main gmock gmock_main PROPERTIES - FOLDER External/GoogleTest - ) - target_compile_options(gtest PRIVATE $<$:-Wno-character-conversion>) - target_compile_options(gmock PRIVATE $<$:-Wno-character-conversion>) - endif() - - # --------------------------------------------------------------------------- - # cli11 - # --------------------------------------------------------------------------- - CPMAddPackage( - URI "gh:CLIUtils/CLI11@${CLI11_VERSION}#${CLI11_DIGEST}" - NAME cli11 - ) - - # --------------------------------------------------------------------------- - # libfmt - # --------------------------------------------------------------------------- - CPMAddPackage( - URI "gh:fmtlib/fmt@${LIBFMT_VERSION}#${LIBFMT_DIGEST}" - NAME libfmt + URI "gh:cucumber/pretty-formatter@${CUCUMBER_PRETTY_FORMATTER_VERSION}#${CUCUMBER_PRETTY_FORMATTER_DIGEST}" + NAME cucumber_pretty_formatter + SOURCE_SUBDIR cpp ) # --------------------------------------------------------------------------- - # pugixml + # cucumber_cucumber_expressions # --------------------------------------------------------------------------- CPMAddPackage( - URI "gh:zeux/pugixml@${PUGIXML_VERSION}#${PUGIXML_DIGEST}" - NAME pugixml + URI "gh:cucumber/cucumber-expressions@${CUCUMBER_CUCUMBER_EXPRESSIONS_VERSION}#${CUCUMBER_CUCUMBER_EXPRESSIONS_DIGEST}" + NAME cucumber_cucumber_expressions + SOURCE_SUBDIR cpp ) - - if (CCR_USE_RE2) - # --------------------------------------------------------------------------- - # abseil-cpp - # --------------------------------------------------------------------------- - CPMAddPackage( - URI "gh:abseil/abseil-cpp@${ABSEIL_CPP_VERSION}#${ABSEIL_CPP_DIGEST}" - NAME abseil-cpp - OPTIONS - "ABSL_PROPAGATE_CXX_STD ON" - "ABSL_ENABLE_INSTALL ON" - ) - - # --------------------------------------------------------------------------- - # re2 - # --------------------------------------------------------------------------- - CPMAddPackage( - URI "gh:google/re2#${RE2_DIGEST}" - NAME re2 - OPTIONS - "RE2_BUILD_TESTING OFF" - ) - endif() - - if (CCR_BUILD_TESTS) - # --------------------------------------------------------------------------- - # yaml-cpp - # --------------------------------------------------------------------------- - CPMAddPackage( - URI "gh:jbeder/yaml-cpp@${YAML_CPP_VERSION}#${YAML_CPP_DIGEST}" - NAME yaml-cpp - OPTIONS - "YAML_ENABLE_PIC OFF" - ) - endif() else() - find_package(CLI11 ${CLI11_VERSION} REQUIRED) - find_package(nlohmann_json ${NLOHMANN_JSON_VERSION} REQUIRED) - find_package(cucumber_messages ${CUCUMBER_MESSAGES_VERSION} REQUIRED) - find_package(cucumber_gherkin ${CUCUMBER_GHERKIN_VERSION} REQUIRED) - find_package(cucumber_query ${CUCUMBER_QUERY_VERSION} REQUIRED) - find_package(cucumber_tag_expressions ${CUCUMBER_TAG_EXPRESSIONS_VERSION} REQUIRED) + find_package(CLI11 REQUIRED) + find_package(nlohmann_json REQUIRED) find_package(GTest REQUIRED) - find_package(pugixml ${PUGIXML_VERSION} REQUIRED) - find_package(fmt ${LIBFMT_VERSION} REQUIRED) - - if (CCR_USE_RE2) - find_package(absl ${ABSEIL_CPP_VERSION}) - find_package(re2) - endif() - - if (CCR_BUILD_TESTS) - find_package(yaml-cpp ${YAML_CPP_VERSION} REQUIRED) - endif() + find_package(pugixml REQUIRED) + find_package(fmt 10 REQUIRED) + + find_package(cucumber_messages REQUIRED) + find_package(cucumber_gherkin REQUIRED) + find_package(cucumber_query REQUIRED) + find_package(cucumber_tag_expressions REQUIRED) + find_package(cucumber_pretty_formatter REQUIRED) + find_package(cucumber_cucumber_expressions REQUIRED) endif() diff --git a/cucumber_cpp/CucumberCpp.hpp b/cucumber_cpp/CucumberCpp.hpp index f7a56f7a..ae5e1058 100644 --- a/cucumber_cpp/CucumberCpp.hpp +++ b/cucumber_cpp/CucumberCpp.hpp @@ -6,14 +6,13 @@ #include "cucumber_cpp/library/Hooks.hpp" #include "cucumber_cpp/library/Parameter.hpp" #include "cucumber_cpp/library/Steps.hpp" -#include "cucumber_cpp/library/cucumber_expression/MatchRange.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber_cpp/library/engine/Step.hpp" namespace cucumber_cpp { + using cucumber::cucumber_expressions::StringTo; using cucumber_cpp::library::Application; using cucumber_cpp::library::Context; - using cucumber_cpp::library::cucumber_expression::StringTo; using cucumber_cpp::library::engine::StepBase; } diff --git a/cucumber_cpp/Steps.hpp b/cucumber_cpp/Steps.hpp index 0c438194..ee8312aa 100644 --- a/cucumber_cpp/Steps.hpp +++ b/cucumber_cpp/Steps.hpp @@ -5,8 +5,6 @@ #include "cucumber_cpp/library/Hooks.hpp" #include "cucumber_cpp/library/Parameter.hpp" #include "cucumber_cpp/library/Steps.hpp" -#include "cucumber_cpp/library/cucumber_expression/MatchRange.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/engine/ExecutionContext.hpp" #include "cucumber_cpp/library/engine/Hook.hpp" #include "cucumber_cpp/library/engine/Step.hpp" @@ -15,8 +13,8 @@ namespace cucumber_cpp { + using cucumber::cucumber_expressions::StringTo; using cucumber_cpp::library::Context; - using cucumber_cpp::library::cucumber_expression::StringTo; using cucumber_cpp::library::engine::AttachOptions; using cucumber_cpp::library::engine::HookBase; using cucumber_cpp::library::engine::StepBase; diff --git a/cucumber_cpp/acceptance_test/hooks/Hooks.cpp b/cucumber_cpp/acceptance_test/hooks/Hooks.cpp index 631f2744..31de9586 100644 --- a/cucumber_cpp/acceptance_test/hooks/Hooks.cpp +++ b/cucumber_cpp/acceptance_test/hooks/Hooks.cpp @@ -38,7 +38,7 @@ HOOK_AFTER_STEP("@stephook and @bats") std::cout << "HOOK_AFTER_STEP\n"; } -HOOK_BEFORE_SCENARIO("@fail_scenariohook_before") +HOOK_BEFORE_SCENARIO("@fail_scenariohook_before", "will fail before scenario") { FAIL(); } @@ -53,7 +53,7 @@ HOOK_BEFORE_SCENARIO("@throw_scenariohook") throw std::string{ "error" }; } -HOOK_BEFORE_SCENARIO(.name = "fail if --failprogramhook is set") +HOOK_BEFORE_SCENARIO("@smoke and @result:OK", "fail if --failprogramhook is set") { if (context.Contains("--failprogramhook") && context.Get("--failprogramhook")) std::cout << "should not be executed\n"; diff --git a/cucumber_cpp/acceptance_test/test.bats b/cucumber_cpp/acceptance_test/test.bats index e433289a..bc7b4580 100644 --- a/cucumber_cpp/acceptance_test/test.bats +++ b/cucumber_cpp/acceptance_test/test.bats @@ -1,8 +1,9 @@ #!/usr/bin/env bats setup_file() { - acceptance_test=$(find . -name "cucumber_cpp.acceptance_test" -not -name "*.plugin*" -print -quit) - plugin_test=$(find . -name "cucumber_cpp.acceptance_test.plugin" -not -name "*.plugin_*" -print -quit) + # select last build executable + acceptance_test=$(find . -name "cucumber_cpp.acceptance_test" -not -name "*.plugin*" -printf '%T@ %p\n' | sort -nr | head -n1 | cut -d' ' -f2-) + plugin_test=$(find . -name "cucumber_cpp.acceptance_test.plugin" -not -name "*.plugin_*" -printf '%T@ %p\n' | sort -nr | head -n1 | cut -d' ' -f2-) export acceptance_test plugin_test } @@ -33,8 +34,8 @@ teardown() { @test "■ tests" { run $acceptance_test --format summary --format-options "{ \"summary\": {\"theme\":\"plain\"} }" --tags "@result:UNDEFINED" -- cucumber_cpp/acceptance_test/features assert_failure - assert_output --partial "■ Given a missing step" - assert_output --partial "↷ Then this should be skipped" + assert_output --partial "Undefined scenarios:" + assert_output --partial "Given a missing step" } @test "No tests" { @@ -121,7 +122,7 @@ teardown() { run $acceptance_test --format summary --format-options "{ \"summary\": {\"theme\":\"plain\"} }" --tags "@result:UNDEFINED" --dry-run cucumber_cpp/acceptance_test/features assert_success - assert_output --partial "■ Given a missing step" + assert_output --partial "Given a missing step" } @test "Test the and keyword" { @@ -155,19 +156,22 @@ teardown() { @test "Test failing hook before results in error" { run $acceptance_test --format summary --format-options "{ \"summary\": {\"theme\":\"plain\"} }" --tags "@fail_scenariohook_before" -- cucumber_cpp/acceptance_test/features assert_failure - assert_output --partial "✘ Before" + assert_output --partial "Failed scenarios:" + assert_output --partial "Before(will fail before scenario) #" } @test "Test failing hook after results in error" { run $acceptance_test --format summary --format-options "{ \"summary\": {\"theme\":\"plain\"} }" --tags "@fail_scenariohook_after" -- cucumber_cpp/acceptance_test/features assert_failure - assert_output --partial "✘ After" + assert_output --partial "Failed scenarios:" + assert_output --partial "After #" } @test "Test throwing hook results in error" { run $acceptance_test --format summary --format-options "{ \"summary\": {\"theme\":\"plain\"} }" --tags "@throw_scenariohook" -- cucumber_cpp/acceptance_test/features assert_failure - assert_output --partial "✘ Before" + assert_output --partial "Failed scenarios:" + assert_output --partial "Before #" } @test "Test error program hook results in error and skipped steps" { @@ -228,8 +232,9 @@ teardown() { run $acceptance_test --format summary --format-options "{ \"summary\": {\"theme\":\"plain\"} }" --tags "@fail_step_fixture" -- cucumber_cpp/acceptance_test/features assert_failure assert_output --partial "key not found: \"nonExistentKey\"" - assert_output --partial "2 scenarios 1 passed, 1 failed" - assert_output --partial "2 steps 1 passed, 1 failed" + assert_output --partial "2 hooks (2 passed)" + assert_output --partial "2 scenarios (1 passed, 1 failed)" + assert_output --partial "2 steps (1 passed, 1 failed)" } @test "Test nested failures propagate properly" { @@ -239,7 +244,7 @@ teardown() { assert_output --partial "Value of: false" assert_output --partial "Expected: is true" assert_output --partial "Actual: false (of type bool)" - assert_output --partial "↷ Then this should be skipped" + assert_output --partial "2 steps (1 skipped, 1 failed)" } @test "Test providing access to scenario info in scenario and step hooks" { diff --git a/cucumber_cpp/library/Application.cpp b/cucumber_cpp/library/Application.cpp index c5d591f9..87409af6 100644 --- a/cucumber_cpp/library/Application.cpp +++ b/cucumber_cpp/library/Application.cpp @@ -4,21 +4,20 @@ #include "CLI/Error.hpp" #include "CLI/Option.hpp" #include "CLI/Validators.hpp" -#include "CLI/impl/App_inl.hpp" +#include "cucumber/cucumber-expressions/Errors.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber/gherkin/Demangle.hpp" #include "cucumber/tag-expressions/Parser.hpp" #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/Errors.hpp" #include "cucumber_cpp/library/api/Formatters.hpp" #include "cucumber_cpp/library/api/RunCucumber.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/plugin/DynamicLibraryManager.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" #include "cucumber_cpp/library/support/Types.hpp" #include "cucumber_cpp/library/util/Duration.hpp" #include "cucumber_cpp/library/util/Timestamp.hpp" -#include "fmt/base.h" +#include "fmt/core.h" #include "fmt/format.h" #include "fmt/ranges.h" #include @@ -44,7 +43,7 @@ namespace cucumber_cpp::library : manager{ manager } { support::DefinitionRegistration::Instance().TakeSnapshot(); - cucumber_expression::ConverterRegistry::TakeSnapshot(); + cucumber::cucumber_expressions::ConverterRegistry::TakeSnapshot(); try { @@ -76,7 +75,7 @@ namespace cucumber_cpp::library void Cleanup() { support::DefinitionRegistration::Instance().UnregisterPlugins(); - cucumber_expression::ConverterRegistry::RestoreSnapshot(); + cucumber::cucumber_expressions::ConverterRegistry::RestoreSnapshot(); manager.UnloadAll(); } @@ -203,7 +202,7 @@ namespace cucumber_cpp::library std::cout << fmt::format("InternalError error:\n{}\n", error.what()); return EXIT_FAILURE; } - catch (const cucumber_expression::Error& error) + catch (const cucumber::cucumber_expressions::Error& error) { std::cout << fmt::format("Cucumber Expression error:\n{}\n", error.what()); return EXIT_FAILURE; @@ -230,7 +229,7 @@ namespace cucumber_cpp::library return programContextRef; } - cucumber_expression::ParameterRegistry& Application::ParameterRegistration() + cucumber::cucumber_expressions::ParameterRegistry& Application::ParameterRegistration() { return parameterRegistry; } diff --git a/cucumber_cpp/library/Application.hpp b/cucumber_cpp/library/Application.hpp index 1eca6a7c..968b3db1 100644 --- a/cucumber_cpp/library/Application.hpp +++ b/cucumber_cpp/library/Application.hpp @@ -4,9 +4,9 @@ // IWYU pragma: private, include "cucumber_cpp/CucumberCpp.hpp" // IWYU pragma: friend cucumber_cpp/.* +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/api/Formatters.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/plugin/DynamicLibraryManager.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" @@ -72,7 +72,7 @@ namespace cucumber_cpp::library CLI::App& CliParser(); Context& ProgramContext(); - cucumber_expression::ParameterRegistry& ParameterRegistration(); + cucumber::cucumber_expressions::ParameterRegistry& ParameterRegistration(); api::Formatters& Formatters(); private: @@ -93,7 +93,7 @@ namespace cucumber_cpp::library plugin::DynamicLibraryManager dynamicLibraryManager; - cucumber_expression::ParameterRegistry parameterRegistry{ cucumber_cpp::library::support::DefinitionRegistration::Instance().GetRegisteredParameters() }; + cucumber::cucumber_expressions::ParameterRegistry parameterRegistry{ cucumber_cpp::library::support::DefinitionRegistration::Instance().GetRegisteredParameters() }; bool removeDefaultGoogleTestListener; std::unique_ptr stopwatch; std::unique_ptr timestampGenerator; diff --git a/cucumber_cpp/library/BodyMacro.hpp b/cucumber_cpp/library/BodyMacro.hpp index c345d433..ed36a52f 100644 --- a/cucumber_cpp/library/BodyMacro.hpp +++ b/cucumber_cpp/library/BodyMacro.hpp @@ -1,8 +1,8 @@ #ifndef CUCUMBER_CPP_BODYMACRO_HPP #define CUCUMBER_CPP_BODYMACRO_HPP -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/Argument.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/Body.hpp" #include "cucumber_cpp/library/util/Body.hpp" #include "cucumber_cpp/library/util/TestStepResult.hpp" @@ -15,9 +15,9 @@ namespace cucumber_cpp::library::detail { template - T TransformArg(const std::string& parameterName, const cucumber_expression::ConvertFunctionArg& parameterArgs) + T TransformArg(const std::string& parameterName, const cucumber::cucumber_expressions::ConvertFunctionArg& parameterArgs) { - return cucumber_expression::TransformArg(T{}, parameterName, parameterArgs); + return cucumber::cucumber_expressions::TransformArg(T{}, parameterName, parameterArgs); } template diff --git a/cucumber_cpp/library/CMakeLists.txt b/cucumber_cpp/library/CMakeLists.txt index 977e8246..5a6ebaf2 100644 --- a/cucumber_cpp/library/CMakeLists.txt +++ b/cucumber_cpp/library/CMakeLists.txt @@ -23,7 +23,8 @@ target_link_libraries(cucumber_cpp.library PUBLIC cucumber_cpp.library.plugin cucumber_cpp.library.support cucumber_cpp.library.util - cucumber_gherkin_lib + cucumber::gherkin + cucumber::cucumber_expressions cucumber::tag_expressions GTest::gmock GTest::gtest @@ -39,7 +40,6 @@ target_compile_options(cucumber_cpp.library add_subdirectory(api) add_subdirectory(assemble) -add_subdirectory(cucumber_expression) add_subdirectory(engine) add_subdirectory(formatter) add_subdirectory(plugin) diff --git a/cucumber_cpp/library/Parameter.hpp b/cucumber_cpp/library/Parameter.hpp index 652e2677..48a81795 100644 --- a/cucumber_cpp/library/Parameter.hpp +++ b/cucumber_cpp/library/Parameter.hpp @@ -4,7 +4,7 @@ // IWYU pragma: private, include "cucumber_cpp/Steps.hpp" // IWYU pragma: friend cucumber_cpp/.* -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include #include #include @@ -31,32 +31,32 @@ namespace cucumber_cpp::library::detail #define PARAMETER_STRUCT CONCAT(ParameterImpl, __LINE__) -#define PARAMETER_BODY(Type, Info, Args) \ - namespace \ - { \ - struct PARAMETER_STRUCT \ - { \ - static std::optional Transform(const cucumber_cpp::library::cucumber_expression::ConvertFunctionArg& group) \ - { \ - return TransformFn(group, static_cast(nullptr)); \ - } \ - \ - template \ - static std::optional TransformFn(const cucumber_cpp::library::cucumber_expression::ConvertFunctionArg& group, void (* /* unused */)(TArgs...)) \ - { \ - return TransformFn(group, std::make_index_sequence{}); \ - } \ - \ - template \ - static std::optional TransformFn(const cucumber_cpp::library::cucumber_expression::ConvertFunctionArg& group, std::index_sequence /*unused*/) \ - { \ - return TransformFn(cucumber_cpp::library::detail::TransformParameterArg(std::remove_cvref_t{}, group[I])...); \ - } \ - static std::optional TransformFn Args; \ - static const std::size_t ID; \ - }; \ - } \ - const std::size_t PARAMETER_STRUCT::ID = cucumber_cpp::library::support::DefinitionRegistration::Instance().Register>(cucumber_cpp::library::cucumber_expression::CustomParameterEntryParams Info); \ +#define PARAMETER_BODY(Type, Info, Args) \ + namespace \ + { \ + struct PARAMETER_STRUCT \ + { \ + static std::optional Transform(const cucumber::cucumber_expressions::ConvertFunctionArg& group) \ + { \ + return TransformFn(group, static_cast(nullptr)); \ + } \ + \ + template \ + static std::optional TransformFn(const cucumber::cucumber_expressions::ConvertFunctionArg& group, void (* /* unused */)(TArgs...)) \ + { \ + return TransformFn(group, std::make_index_sequence{}); \ + } \ + \ + template \ + static std::optional TransformFn(const cucumber::cucumber_expressions::ConvertFunctionArg& group, std::index_sequence /*unused*/) \ + { \ + return TransformFn(cucumber_cpp::library::detail::TransformParameterArg(std::remove_cvref_t{}, group[I])...); \ + } \ + static std::optional TransformFn Args; \ + static const std::size_t ID; \ + }; \ + } \ + const std::size_t PARAMETER_STRUCT::ID = cucumber_cpp::library::support::DefinitionRegistration::Instance().Register>(cucumber::cucumber_expressions::CustomParameterEntryParams Info); \ std::optional PARAMETER_STRUCT::TransformFn Args #define PARAMETER(Type, Info, Args) PARAMETER_BODY(Type, Info, Args) diff --git a/cucumber_cpp/library/api/CMakeLists.txt b/cucumber_cpp/library/api/CMakeLists.txt index 08282f68..ca1ff6f9 100644 --- a/cucumber_cpp/library/api/CMakeLists.txt +++ b/cucumber_cpp/library/api/CMakeLists.txt @@ -14,7 +14,7 @@ target_include_directories(cucumber_cpp.library.api PUBLIC ) target_link_libraries(cucumber_cpp.library.api PUBLIC - cucumber_cpp.library.cucumber_expression + cucumber::cucumber_expressions cucumber_cpp.library.formatter cucumber_cpp.library.runtime cucumber_cpp.library.support diff --git a/cucumber_cpp/library/api/Formatters.cpp b/cucumber_cpp/library/api/Formatters.cpp index 549e09b5..f6421cb1 100644 --- a/cucumber_cpp/library/api/Formatters.cpp +++ b/cucumber_cpp/library/api/Formatters.cpp @@ -1,9 +1,7 @@ #include "cucumber_cpp/library/api/Formatters.hpp" -#include "cucumber_cpp/library/formatter/Formatter.hpp" +#include "cucumber_cpp/library/formatter/ExternalFormatter.hpp" #include "cucumber_cpp/library/formatter/JunitXmlFormatter.hpp" #include "cucumber_cpp/library/formatter/MessageFormatter.hpp" -#include "cucumber_cpp/library/formatter/PrettyFormatter.hpp" -#include "cucumber_cpp/library/formatter/SummaryFormatter.hpp" #include "cucumber_cpp/library/formatter/UsageFormatter.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" @@ -24,6 +22,14 @@ namespace cucumber_cpp::library::api { + namespace + { + nlohmann::json SubOptions(const nlohmann::json& formatOptions, const std::string& name) + { + return formatOptions.contains(name) ? formatOptions.at(name) : nlohmann::json::object(); + } + } + FormatterOption::FormatterOption(std::string_view str) { const auto colon = str.find(':'); @@ -33,13 +39,32 @@ namespace cucumber_cpp::library::api Formatters::Formatters() { - RegisterFormatter(); - RegisterFormatter(); + RegisterFormatter("pretty", RegisteredFormatter{ [](support::SupportCodeLibrary&, util::Broadcaster& broadcaster, const nlohmann::json& formatOptions, std::ostream& output) -> std::unique_ptr + { + return std::make_unique(broadcaster, formatter::MakePrettyPrinter(SubOptions(formatOptions, "pretty"), output)); + } }); + RegisterFormatter("summary", RegisteredFormatter{ [](support::SupportCodeLibrary&, util::Broadcaster& broadcaster, const nlohmann::json& formatOptions, std::ostream& output) -> std::unique_ptr + { + return std::make_unique(broadcaster, formatter::MakeSummaryPrinter(SubOptions(formatOptions, "summary"), output)); + } }); + RegisterFormatter("progress", RegisteredFormatter{ [](support::SupportCodeLibrary&, util::Broadcaster& broadcaster, const nlohmann::json& formatOptions, std::ostream& output) -> std::unique_ptr + { + return std::make_unique(broadcaster, formatter::MakeProgressPrinter(SubOptions(formatOptions, "progress"), output)); + } }); + RegisterFormatter("progressbar", RegisteredFormatter{ [](support::SupportCodeLibrary&, util::Broadcaster& broadcaster, const nlohmann::json& formatOptions, std::ostream& output) -> std::unique_ptr + { + return std::make_unique(broadcaster, formatter::MakeProgressBarPrinter(SubOptions(formatOptions, "progress-bar"), output)); + } }); RegisterFormatter(); RegisterFormatter(); RegisterFormatter(); } + void Formatters::RegisterFormatter(std::string name, RegisteredFormatter registeredFormatter) + { + availableFormatters.try_emplace(std::move(name), std::move(registeredFormatter)); + } + std::set> Formatters::GetAvailableFormatterNames() const { auto view = availableFormatters | std::views::transform([](const auto& pair) -> std::pair @@ -49,9 +74,9 @@ namespace cucumber_cpp::library::api return { view.begin(), view.end() }; } - std::list> Formatters::EnableFormatters(const std::set>& format, const nlohmann::json& formatOptions, support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, std::ostream& output) + std::list> Formatters::EnableFormatters(const std::set>& format, const nlohmann::json& formatOptions, support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, std::ostream& output) { - std::list> activeFormatters; + std::list> activeFormatters; for (const auto& formatterName : format) { diff --git a/cucumber_cpp/library/api/Formatters.hpp b/cucumber_cpp/library/api/Formatters.hpp index 26440691..ab8ae8cc 100644 --- a/cucumber_cpp/library/api/Formatters.hpp +++ b/cucumber_cpp/library/api/Formatters.hpp @@ -28,7 +28,7 @@ namespace cucumber_cpp::library::api struct RegisteredFormatter { - std::function(support::SupportCodeLibrary&, util::Broadcaster&, const nlohmann::json& formatOptions, std::ostream&)> factory; + std::function(support::SupportCodeLibrary&, util::Broadcaster&, const nlohmann::json& formatOptions, std::ostream&)> factory; bool hasOutput{ false }; }; @@ -39,9 +39,11 @@ namespace cucumber_cpp::library::api template void RegisterFormatter(bool hasOutput = false); + void RegisterFormatter(std::string name, RegisteredFormatter registeredFormatter); + std::set> GetAvailableFormatterNames() const; - [[nodiscard]] std::list> EnableFormatters(const std::set>& format, const nlohmann::json& formatOptions, support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, std::ostream& output = std::cout); + [[nodiscard]] std::list> EnableFormatters(const std::set>& format, const nlohmann::json& formatOptions, support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, std::ostream& output = std::cout); private: std::map> availableFormatters; @@ -55,7 +57,7 @@ namespace cucumber_cpp::library::api template void Formatters::RegisterFormatter(bool hasOutput) { - availableFormatters.try_emplace(T::name, [](support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, const nlohmann::json& formatOptions, std::ostream& output) + availableFormatters.try_emplace(T::name, [](support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, const nlohmann::json& formatOptions, std::ostream& output) -> std::unique_ptr { return std::make_unique(supportCodeLibrary, broadcaster, formatOptions, output); }, diff --git a/cucumber_cpp/library/api/Gherkin.cpp b/cucumber_cpp/library/api/Gherkin.cpp index b0feb1f4..e5f28ed1 100644 --- a/cucumber_cpp/library/api/Gherkin.cpp +++ b/cucumber_cpp/library/api/Gherkin.cpp @@ -12,13 +12,12 @@ #include "cucumber/messages/SourceReference.hpp" #include "cucumber_cpp/library/support/Types.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" -#include "cucumber_cpp/library/util/MakeShared.hpp" #include #include namespace cucumber_cpp::library::api { - std::list CollectPickles(const support::RunOptions::Sources& sources, cucumber::gherkin::IdGeneratorPtr idGenerator, const util::Broadcaster& broadcaster) + std::list CollectPickles(const support::RunOptions::Sources& sources, cucumber::gherkin::IdGeneratorPtr idGenerator, util::Broadcaster& broadcaster) { std::list pickleSources; @@ -26,18 +25,24 @@ namespace cucumber_cpp::library::api for (const auto& path : sources.paths) { - auto source = std::make_shared(cucumber::messages::Source{ - .uri = path.string(), - .data = cucumber::gherkin::Slurp(path.string()), - }); - const auto uri = source->uri; + const auto uri = path.string(); + const auto data = cucumber::gherkin::Slurp(path.string()); - broadcaster.BroadcastEvent(source); + broadcaster.BroadcastEvent([&uri, &data](cucumber::messages::Envelope& envelope) + { + cucumber::messages::Source source{}; + source.uri = uri; + source.data = data; + envelope.source = std::move(source); + }); try { - auto ast = std::make_shared(parser.Parse(uri, source->data)); - broadcaster.BroadcastEvent(util::MakeShared(*ast)); + auto ast = std::make_shared(parser.Parse(uri, data)); + broadcaster.BroadcastEvent([&ast](cucumber::messages::Envelope& envelope) + { + envelope.gherkinDocument = *ast; + }); cucumber::gherkin::PickleCompiler pc(idGenerator); pc.Compile(*ast, uri, [&pickleSources, ast, &broadcaster](const cucumber::messages::Pickle& pickle) @@ -46,31 +51,42 @@ namespace cucumber_cpp::library::api std::make_shared(pickle), ast); - broadcaster.BroadcastEvent(util::MakeShared(pickle)); + broadcaster.BroadcastEvent([&pickle](cucumber::messages::Envelope& envelope) + { + envelope.pickle = pickle; + }); }); } catch (const cucumber::gherkin::CompositeParserError& compositeError) { for (const auto& error : compositeError.Errors()) - { - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::ParseError{ - .source = util::MakeShared(cucumber::messages::SourceReference{ - .uri = uri, - .location = util::MakeShared(error->Location()), - }), - .message = error->what(), - })); - } + broadcaster.BroadcastEvent([&uri, &error](cucumber::messages::Envelope& envelope) + { + cucumber::messages::SourceReference sourceReference; + sourceReference.uri = uri; + sourceReference.location = error->Location(); + + cucumber::messages::ParseError parseError; + parseError.source = sourceReference; + parseError.message = error->what(); + + envelope.parseError = std::move(parseError); + }); } catch (const cucumber::gherkin::ParserError& error) { - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::ParseError{ - .source = util::MakeShared(cucumber::messages::SourceReference{ - .uri = uri, - .location = util::MakeShared(error.Location()), - }), - .message = error.what(), - })); + broadcaster.BroadcastEvent([&uri, &error](cucumber::messages::Envelope& envelope) + { + cucumber::messages::SourceReference sourceReference; + sourceReference.uri = uri; + sourceReference.location = error.Location(); + + cucumber::messages::ParseError parseError; + parseError.source = sourceReference; + parseError.message = error.what(); + + envelope.parseError = std::move(parseError); + }); } } diff --git a/cucumber_cpp/library/api/Gherkin.hpp b/cucumber_cpp/library/api/Gherkin.hpp index 6c921ee9..7aa31249 100644 --- a/cucumber_cpp/library/api/Gherkin.hpp +++ b/cucumber_cpp/library/api/Gherkin.hpp @@ -8,7 +8,7 @@ namespace cucumber_cpp::library::api { - std::list CollectPickles(const support::RunOptions::Sources& sources, cucumber::gherkin::IdGeneratorPtr idGenerator, const util::Broadcaster& broadcaster); + std::list CollectPickles(const support::RunOptions::Sources& sources, cucumber::gherkin::IdGeneratorPtr idGenerator, util::Broadcaster& broadcaster); } #endif diff --git a/cucumber_cpp/library/api/RunCucumber.cpp b/cucumber_cpp/library/api/RunCucumber.cpp index d21ae8eb..af68644a 100644 --- a/cucumber_cpp/library/api/RunCucumber.cpp +++ b/cucumber_cpp/library/api/RunCucumber.cpp @@ -12,7 +12,6 @@ #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/api/Formatters.hpp" #include "cucumber_cpp/library/api/Gherkin.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/plugin/HookLoader.hpp" #include "cucumber_cpp/library/plugin/ParameterLoader.hpp" #include "cucumber_cpp/library/plugin/StepLoader.hpp" @@ -46,81 +45,109 @@ namespace cucumber_cpp::library::api { namespace { - void EmitParameters(const support::SupportCodeLibrary& supportCodeLibrary, const util::Broadcaster& broadcaster, const cucumber::gherkin::IdGeneratorPtr& idGenerator) + void EmitParameters(const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, const cucumber::gherkin::IdGeneratorPtr& idGenerator) { for (const auto& [name, parameter] : supportCodeLibrary.parameterRegistry.GetParameters()) { if (parameter.isBuiltin) continue; - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::ParameterType{ - .name = parameter.name, - .regularExpressions = parameter.regex, - .useForSnippets = parameter.useForSnippets, - .id = idGenerator->NextId(), - .sourceReference = util::MakeShared(cucumber::messages::SourceReference{ - .uri = parameter.location.file_name(), - .location = util::MakeShared(cucumber::messages::Location{ - .line = parameter.location.line(), - }), - }), - })); + broadcaster.BroadcastEvent([¶meter, &idGenerator](cucumber::messages::Envelope& envelope) + { + cucumber::messages::Location location; + location.line = parameter.location.line(); + + cucumber::messages::SourceReference sourceReference; + sourceReference.uri = parameter.location.file_name(); + sourceReference.location = location; + + cucumber::messages::ParameterType parameterType{}; + parameterType.name = parameter.name; + parameterType.regularExpressions = parameter.regex; + parameterType.useForSnippets = parameter.useForSnippets; + parameterType.id = idGenerator->NextId(); + parameterType.sourceReference = sourceReference; + + envelope.parameterType = std::move(parameterType); + }); } } - void EmitUndefinedParameters(const support::SupportCodeLibrary& supportCodeLibrary, const util::Broadcaster& broadcaster) + void EmitUndefinedParameters(const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster) { for (const auto& parameter : supportCodeLibrary.undefinedParameters.definitions) - broadcaster.BroadcastEvent(util::MakeShared(parameter)); + broadcaster.BroadcastEvent([¶meter](cucumber::messages::Envelope& envelope) + { + envelope.undefinedParameterType = parameter; + }); } - void EmitStepDefinitions(const support::SupportCodeLibrary& supportCodeLibrary, const util::Broadcaster& broadcaster) + void EmitStepDefinitions(const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster) { for (const auto& stepDefinition : supportCodeLibrary.stepRegistry.StepDefinitions()) { - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::StepDefinition{ - .id = stepDefinition.id, - .pattern = util::MakeShared(cucumber::messages::StepDefinitionPattern{ - .source = stepDefinition.pattern, - .type = stepDefinition.patternType == support::ExpressionPatternType::cucumberExpression ? cucumber::messages::StepDefinitionPatternType::CUCUMBER_EXPRESSION : cucumber::messages::StepDefinitionPatternType::REGULAR_EXPRESSION, - }), - .sourceReference = util::MakeShared(cucumber::messages::SourceReference{ - .uri = stepDefinition.uri.string(), - .location = util::MakeShared(cucumber::messages::Location{ - .line = stepDefinition.line, - }), - }), - })); + broadcaster.BroadcastEvent([&stepDefinition](cucumber::messages::Envelope& envelope) + { + cucumber::messages::StepDefinitionPattern pattern; + pattern.source = stepDefinition.pattern; + pattern.type = stepDefinition.patternType == support::ExpressionPatternType::cucumberExpression ? cucumber::messages::StepDefinitionPatternType::CUCUMBER_EXPRESSION : cucumber::messages::StepDefinitionPatternType::REGULAR_EXPRESSION; + + cucumber::messages::Location location; + location.line = stepDefinition.line; + + cucumber::messages::SourceReference sourceReference; + sourceReference.uri = stepDefinition.uri.string(); + sourceReference.location = location; + + cucumber::messages::StepDefinition stepDefinitionMessage; + stepDefinitionMessage.id = stepDefinition.id; + stepDefinitionMessage.pattern = pattern; + stepDefinitionMessage.sourceReference = sourceReference; + + envelope.stepDefinition = std::move(stepDefinitionMessage); + }); } } - void EmitTestCaseHooks(const support::SupportCodeLibrary& supportCodeLibrary, const util::Broadcaster& broadcaster) + void EmitTestCaseHooks(const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster) { auto beforeAllHooks = supportCodeLibrary.hookRegistry.HooksByType(util::HookType::before); for (const auto& hook : beforeAllHooks) - broadcaster.BroadcastEvent(util::MakeShared(util::TransformHookData(hook))); + broadcaster.BroadcastEvent([&hook](cucumber::messages::Envelope& envelope) + { + envelope.hook = util::TransformHookData(hook); + }); auto afterAllHooks = supportCodeLibrary.hookRegistry.HooksByType(util::HookType::after); for (const auto& hook : afterAllHooks) - broadcaster.BroadcastEvent(util::MakeShared(util::TransformHookData(hook))); + broadcaster.BroadcastEvent([&hook](cucumber::messages::Envelope& envelope) + { + envelope.hook = util::TransformHookData(hook); + }); } - void EmitTestRunHooks(const support::SupportCodeLibrary& supportCodeLibrary, const util::Broadcaster& broadcaster) + void EmitTestRunHooks(const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster) { auto beforeAllHooks = supportCodeLibrary.hookRegistry.HooksByType(util::HookType::beforeAll); for (const auto& hook : beforeAllHooks) - broadcaster.BroadcastEvent(util::MakeShared(util::TransformHookData(hook))); + broadcaster.BroadcastEvent([&hook](cucumber::messages::Envelope& envelope) + { + envelope.hook = util::TransformHookData(hook); + }); auto afterAllHooks = supportCodeLibrary.hookRegistry.HooksByType(util::HookType::afterAll); for (const auto& hook : afterAllHooks) - broadcaster.BroadcastEvent(util::MakeShared(util::TransformHookData(hook))); + broadcaster.BroadcastEvent([&hook](cucumber::messages::Envelope& envelope) + { + envelope.hook = util::TransformHookData(hook); + }); } - void EmitSupportCodeMessages(const support::SupportCodeLibrary& supportCodeLibrary, const util::Broadcaster& broadcaster, const cucumber::gherkin::IdGeneratorPtr& idGenerator) + void EmitSupportCodeMessages(const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, const cucumber::gherkin::IdGeneratorPtr& idGenerator) { // Phase 1: Load parameters (must be first, steps reference parameter types) plugin::ParameterLoader::Load(support::DefinitionRegistration::Instance(), supportCodeLibrary.parameterRegistry); @@ -140,12 +167,12 @@ namespace cucumber_cpp::library::api EmitTestRunHooks(supportCodeLibrary, broadcaster); } - const std::string& TransformPickleTagName(const std::shared_ptr& tag) + const std::string& TransformPickleTagName(const cucumber::messages::PickleTag& tag) { - return tag->name; + return tag.name; } - std::set> PickleTagsToSet(const std::vector>& tags) + std::set> PickleTagsToSet(const std::vector& tags) { auto tagNames = tags | std::views::transform(TransformPickleTagName); return { std::begin(tagNames), std::end(tagNames) }; @@ -184,7 +211,7 @@ namespace cucumber_cpp::library::api void OnEvent(const cucumber::messages::Envelope& envelope) { if (envelope.parseError) - parseErrors.push_back(*envelope.parseError.value()); + parseErrors.push_back(envelope.parseError.value()); } [[nodiscard]] const std::vector& GetParseErrors() const @@ -197,7 +224,7 @@ namespace cucumber_cpp::library::api }; } - bool RunCucumber(const support::RunOptions& options, cucumber_expression::ParameterRegistry& parameterRegistry, Context& programContext, util::Broadcaster& broadcaster, Formatters& formatters, const std::set>& format, const std::string& formatOptions) + bool RunCucumber(const support::RunOptions& options, cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry, Context& programContext, util::Broadcaster& broadcaster, Formatters& formatters, const std::set>& format, const std::string& formatOptions) { cucumber::gherkin::IdGeneratorPtr idGenerator = std::make_shared(); @@ -222,9 +249,9 @@ namespace cucumber_cpp::library::api { for (const auto& parseError : parseErrors) { - const auto uri = parseError.source->uri.value_or("unknown source"); - const auto line = parseError.source->location.has_value() ? fmt::format(":{}", parseError.source->location.value()->line) : ""; - const auto column = parseError.source->location.has_value() && parseError.source->location.value()->column.has_value() ? fmt::format(":{}", parseError.source->location.value()->column.value()) : ""; + const auto uri = parseError.source.uri.value_or("unknown source"); + const auto line = parseError.source.location.has_value() ? fmt::format(":{}", parseError.source.location.value().line) : ""; + const auto column = parseError.source.location.has_value() && parseError.source.location.value().column.has_value() ? fmt::format(":{}", parseError.source.location.value().column.value()) : ""; const auto messageStart = parseError.message.find(": "); const auto message = messageStart != std::string::npos ? parseError.message.substr(messageStart + 2) : parseError.message; diff --git a/cucumber_cpp/library/api/RunCucumber.hpp b/cucumber_cpp/library/api/RunCucumber.hpp index 34ac1976..a776477f 100644 --- a/cucumber_cpp/library/api/RunCucumber.hpp +++ b/cucumber_cpp/library/api/RunCucumber.hpp @@ -1,9 +1,9 @@ #ifndef API_RUN_CUCUMBER_HPP #define API_RUN_CUCUMBER_HPP +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/Context.hpp" #include "cucumber_cpp/library/api/Formatters.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/Types.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" #include @@ -12,7 +12,7 @@ namespace cucumber_cpp::library::api { - bool RunCucumber(const support::RunOptions& options, cucumber_expression::ParameterRegistry& parameterRegistry, Context& programContext, util::Broadcaster& broadcaster, Formatters& formatters, const std::set>& format, const std::string& formatOptions); + bool RunCucumber(const support::RunOptions& options, cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry, Context& programContext, util::Broadcaster& broadcaster, Formatters& formatters, const std::set>& format, const std::string& formatOptions); } #endif diff --git a/cucumber_cpp/library/assemble/AssembleTestSuites.cpp b/cucumber_cpp/library/assemble/AssembleTestSuites.cpp index f73b3ce3..e4f1eeff 100644 --- a/cucumber_cpp/library/assemble/AssembleTestSuites.cpp +++ b/cucumber_cpp/library/assemble/AssembleTestSuites.cpp @@ -1,84 +1,48 @@ #include "cucumber_cpp/library/assemble/AssembleTestSuites.hpp" #include "cucumber/gherkin/IdGenerator.hpp" #include "cucumber/messages/Envelope.hpp" -#include "cucumber/messages/Group.hpp" -#include "cucumber/messages/StepMatchArgument.hpp" #include "cucumber/messages/StepMatchArgumentsList.hpp" #include "cucumber/messages/TestCase.hpp" #include "cucumber/messages/TestStep.hpp" #include "cucumber_cpp/library/assemble/AssembledTestSuite.hpp" -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Matcher.hpp" #include "cucumber_cpp/library/support/HookRegistry.hpp" +#include "cucumber_cpp/library/support/MatchStepDefinitions.hpp" #include "cucumber_cpp/library/support/StepRegistry.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" #include "cucumber_cpp/library/support/Types.hpp" -#include "cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" #include "cucumber_cpp/library/util/HookData.hpp" -#include "cucumber_cpp/library/util/MakeShared.hpp" #include "cucumber_cpp/library/util/TransformPickleTag.hpp" -#include +#include #include #include -#include -#include #include -#include #include #include #include -#include #include namespace cucumber_cpp::library::assemble { namespace { - auto TransformToMatch(const std::string& text) + void AssembleSteps(const support::SupportCodeLibrary& supportCodeLibrary, const support::PickleSource& pickleSource, cucumber::messages::TestCase& testCase, cucumber::gherkin::IdGeneratorBase& idGenerator) { - return [&text](const support::StepRegistry::Definition& definition) -> std::pair>> - { - const auto match = std::visit(cucumber_expression::MatchVisitor{ text }, definition.regex); - return { definition.id, match }; - }; - } + const auto& stepDefinitions = supportCodeLibrary.stepRegistry.StepDefinitions(); - bool HasMatch(const std::pair>>& pair) - { - return pair.second.has_value(); - } - - void AssembleSteps(const support::SupportCodeLibrary& supportCodeLibrary, const support::PickleSource& pickleSource, cucumber::messages::TestCase& testCase, cucumber::gherkin::IdGeneratorPtr idGenerator) - { - for (const auto& step : pickleSource.pickle->steps) + for (const auto& pickleStep : pickleSource.pickle->steps) { - const auto& stepDefinitions = supportCodeLibrary.stepRegistry.StepDefinitions(); - - const auto& testStep = testCase.testSteps.emplace_back(std::make_shared(cucumber::messages::TestStep{ - .hookId = std::nullopt, - .id = idGenerator->NextId(), - .pickleStepId = step->id, - .stepDefinitionIds = std::vector{}, - .stepMatchArgumentsLists = std::vector>{}, - })); - - for (const auto& [id, match] : stepDefinitions | - std::views::transform(TransformToMatch(step->text)) | - std::views::filter(HasMatch)) - { - testStep->stepDefinitionIds.value().push_back(id); - const auto& argumentList = testStep->stepMatchArgumentsLists.value().emplace_back(std::make_shared()); - for (const auto& result : *match) - argumentList->stepMatchArguments.emplace_back(std::make_shared(cucumber::messages::StepMatchArgument{ - .group = std::make_shared(util::ArgumentGroupToMessageGroup(result.Group())), - .parameterTypeName = result.Name().empty() ? std::nullopt : std::make_optional(result.Name()), - })); - } + cucumber::messages::TestStep testStep; + testStep.id = idGenerator.NextId(); + testStep.pickleStepId = pickleStep.id; + testStep.stepDefinitionIds = std::vector{}; + testStep.stepMatchArgumentsLists = std::vector{}; + + support::MatchStepDefinitions(testCase.testSteps.emplace_back(std::move(testStep)), stepDefinitions, pickleStep.text); } } - void AssembleTestSteps(const support::SupportCodeLibrary& supportCodeLibrary, const support::PickleSource& pickleSource, cucumber::messages::TestCase& testCase, cucumber::gherkin::IdGeneratorPtr idGenerator) + void AssembleTestSteps(const support::SupportCodeLibrary& supportCodeLibrary, const support::PickleSource& pickleSource, cucumber::messages::TestCase& testCase, cucumber::gherkin::IdGeneratorBase& idGenerator) { auto beforeHooks = supportCodeLibrary.hookRegistry.FindIds(util::HookType::before, util::TransformPickleTags(pickleSource.pickle->tags)); auto afterHooks = supportCodeLibrary.hookRegistry.FindIds(util::HookType::after, util::TransformPickleTags(pickleSource.pickle->tags)); @@ -86,36 +50,47 @@ namespace cucumber_cpp::library::assemble testCase.testSteps.reserve(beforeHooks.size() + pickleSource.pickle->steps.size() + afterHooks.size()); for (const auto& hookId : beforeHooks) - testCase.testSteps.emplace_back(std::make_shared(cucumber::messages::TestStep{ .hookId = hookId, .id = idGenerator->NextId() })); + { + cucumber::messages::TestStep testStep; + testStep.hookId = hookId; + testStep.id = idGenerator.NextId(); + testCase.testSteps.push_back(testStep); + } AssembleSteps(supportCodeLibrary, pickleSource, testCase, idGenerator); for (const auto& hookId : afterHooks | std::views::reverse) - testCase.testSteps.emplace_back(std::make_shared(cucumber::messages::TestStep{ .hookId = hookId, .id = idGenerator->NextId() })); + { + cucumber::messages::TestStep testStep; + testStep.hookId = hookId; + testStep.id = idGenerator.NextId(); + testCase.testSteps.push_back(testStep); + } } } std::vector AssembleTestSuites(const support::SupportCodeLibrary& supportCodeLibrary, std::string_view testRunStartedId, - const util::Broadcaster& broadcaster, + util::Broadcaster& broadcaster, const std::list& sourcedPickles, - cucumber::gherkin::IdGeneratorPtr idGenerator) + cucumber::gherkin::IdGeneratorBase& idGenerator) { std::list testUris; std::map> assembledTestSuiteMap; for (const auto& pickleSource : sourcedPickles) { - cucumber::messages::TestCase testCase{ - .id = idGenerator->NextId(), - .pickleId = pickleSource.pickle->id, - .testSteps = {}, - .testRunStartedId = std::make_optional(testRunStartedId) - }; + cucumber::messages::TestCase testCase; + testCase.id = idGenerator.NextId(); + testCase.pickleId = pickleSource.pickle->id; + testCase.testRunStartedId = std::make_optional(testRunStartedId); AssembleTestSteps(supportCodeLibrary, pickleSource, testCase, idGenerator); - broadcaster.BroadcastEvent(util::MakeShared(testCase)); + broadcaster.BroadcastEvent([&testCase](cucumber::messages::Envelope& envelope) + { + envelope.testCase = testCase; + }); if (!assembledTestSuiteMap.contains(pickleSource.gherkinDocument->uri.value())) { diff --git a/cucumber_cpp/library/assemble/AssembleTestSuites.hpp b/cucumber_cpp/library/assemble/AssembleTestSuites.hpp index 4c6656f0..dc810904 100644 --- a/cucumber_cpp/library/assemble/AssembleTestSuites.hpp +++ b/cucumber_cpp/library/assemble/AssembleTestSuites.hpp @@ -15,9 +15,9 @@ namespace cucumber_cpp::library::assemble std::vector AssembleTestSuites( const support::SupportCodeLibrary& supportCodeLibrary, std::string_view testRunStartedId, - const util::Broadcaster& broadcaster, + util::Broadcaster& broadcaster, const std::list& sourcedPickles, - cucumber::gherkin::IdGeneratorPtr idGenerator); + cucumber::gherkin::IdGeneratorBase& idGenerator); } #endif diff --git a/cucumber_cpp/library/assemble/CMakeLists.txt b/cucumber_cpp/library/assemble/CMakeLists.txt index 1c038f88..18c4ea76 100644 --- a/cucumber_cpp/library/assemble/CMakeLists.txt +++ b/cucumber_cpp/library/assemble/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(cucumber_cpp.library.assemble PUBLIC ) target_link_libraries(cucumber_cpp.library.assemble PUBLIC - cucumber_cpp.library.cucumber_expression + cucumber::cucumber_expressions cucumber_cpp.library.support cucumber_cpp.library.util ) diff --git a/cucumber_cpp/library/cucumber_expression/Argument.cpp b/cucumber_cpp/library/cucumber_expression/Argument.cpp deleted file mode 100644 index 9c6d420d..00000000 --- a/cucumber_cpp/library/cucumber_expression/Argument.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "fmt/format.h" -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - Argument::Argument(ArgumentGroup group, const ParameterType& parameter) - : group{ std::move(group) } - , parameter{ parameter } - {} - - std::vector Argument::BuildArguments(const ArgumentGroup& group, std::span parameters) - { - if (group.children.size() != parameters.size()) - throw std::runtime_error(fmt::format("Mismatch between number of groups ({}) and parameters ({})", group.children.size(), parameters.size())); - - std::size_t index{ 0 }; - auto converted = parameters | std::views::transform([&group, &index](const ParameterType& parameter) -> Argument - { - return { group.children[index++], parameter }; - }); - - return { converted.begin(), converted.end() }; - } - - ArgumentGroup Argument::Group() const - { - return group; - } - - std::string Argument::Name() const - { - return parameter.name; - } - -} diff --git a/cucumber_cpp/library/cucumber_expression/Argument.hpp b/cucumber_cpp/library/cucumber_expression/Argument.hpp deleted file mode 100644 index 1b8ef2c2..00000000 --- a/cucumber_cpp/library/cucumber_expression/Argument.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_ARGUMENT_HPP -#define CUCUMBER_EXPRESSION_ARGUMENT_HPP - -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - template - T TransformArg([[maybe_unused]] const T& _, const std::string& name, const cucumber_expression::ConvertFunctionArg& match) - { - return ConverterTypeMap>::Instance().At(name)(match).value(); - } - - template - std::optional TransformArg([[maybe_unused]] const std::optional& _, const std::string& name, const cucumber_expression::ConvertFunctionArg& match) - { - return ConverterTypeMap>::Instance().At(name)(match); - } - - struct Argument - { - private: - Argument(ArgumentGroup group, const ParameterType& parameter); - - public: - static std::vector BuildArguments(const ArgumentGroup& group, std::span parameters); - - template - T GetValue() const - { - return TransformArg(T{}, Name(), group.Values()); - } - - [[nodiscard]] ArgumentGroup Group() const; - [[nodiscard]] std::string Name() const; - - private: - ArgumentGroup group; - const ParameterType& parameter; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/Ast.cpp b/cucumber_cpp/library/cucumber_expression/Ast.cpp deleted file mode 100644 index 79542357..00000000 --- a/cucumber_cpp/library/cucumber_expression/Ast.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - const std::map& DemarcationMap() - { - static const std::map demarcationMap = { - { '/', TokenType::alternation }, - { '{', TokenType::beginParameter }, - { '}', TokenType::endParameter }, - { '(', TokenType::beginOptional }, - { ')', TokenType::endOptional }, - }; - - return demarcationMap; - } - } - - Node::Node(NodeType type, - std::size_t start, - std::size_t end, - std::variant> children) - : type{ type } - , start{ start } - , end{ end } - , children{ std::move(children) } - {} - - NodeType Node::Type() const - { - return type; - } - - std::size_t Node::Start() const - { - return start; - } - - std::size_t Node::End() const - { - return end; - } - - std::string Node::Text() - { - return static_cast(this)->Text(); - } - - std::string Node::Text() const - { - if (std::holds_alternative(children)) - return std::get(children); - - if (!std::get>(children).empty()) - { - std::string text{ std::get>(children).front().Text() }; - for (const auto& child : std::get>(children) | std::views::drop(1)) - text += child.Text(); - return text; - } - - return {}; - } - - std::vector& Node::Children() - { - return std::get>(children); - } - - const std::vector& Node::Children() const - { - return std::get>(children); - } - - const std::variant>& Node::GetLeafNodes() const - { - return children; - } - - Token::Token(TokenType type, std::string text, std::size_t start, std::size_t end) - : type{ type } - , text{ std::move(text) } - , start{ start } - , end{ end } - {} - - TokenType Token::Type() const - { - return type; - } - - std::string Token::Text() const - { - return text; - } - - std::size_t Token::Start() const - { - return start; - } - - std::size_t Token::End() const - { - return end; - } - - bool Token::IsEscapeCharacter(char ch) - { - return ch == '\\'; - } - - TokenType Token::TypeOf(char ch) - { - if (std::isspace(static_cast(ch)) != 0) - return TokenType::whiteSpace; - - if (DemarcationMap().contains(ch)) - return DemarcationMap().at(ch); - - return TokenType::text; - } - - bool Token::CanEscape(char ch) - { - if (std::isspace(static_cast(ch)) != 0) - return true; - - return ch == '\\' || DemarcationMap().contains(ch); - } - - std::string Token::NameOf(TokenType type) - { - switch (type) - { - case TokenType::startOfLine: - return "startOfLine"; - case TokenType::endOfLine: - return "endOfLine"; - case TokenType::whiteSpace: - return "whiteSpace"; - case TokenType::beginOptional: - return "beginOptional"; - case TokenType::endOptional: - return "endOptional"; - case TokenType::beginParameter: - return "beginParameter"; - case TokenType::endParameter: - return "endParameter"; - case TokenType::alternation: - return "alternation"; - case TokenType::text: - return "text"; - case TokenType::invalid: - return "invalid"; - } - - throw InvalidTokenType{ "Invalid token type" }; - } - - std::string Token::SymbolOf(TokenType type) - { - for (const auto& [key, value] : DemarcationMap()) - if (value == type) - return { key }; - return ""; - } - - std::string Token::PurposeOf(TokenType type) - { - if (type == TokenType::beginOptional || type == TokenType::endOptional) - return "optional text"; - if (type == TokenType::beginParameter || type == TokenType::endParameter) - return "a parameter"; - if (type == TokenType::alternation) - return "alternation"; - return ""; - } -} diff --git a/cucumber_cpp/library/cucumber_expression/Ast.hpp b/cucumber_cpp/library/cucumber_expression/Ast.hpp deleted file mode 100644 index f728983f..00000000 --- a/cucumber_cpp/library/cucumber_expression/Ast.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_AST_HPP -#define CUCUMBER_EXPRESSION_AST_HPP - -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - enum class NodeType : std::uint8_t - { - text, - optional, - alternation, - alternative, - parameter, - expression, - }; - - enum class TokenType : std::uint8_t - { - startOfLine, - endOfLine, - whiteSpace, - beginOptional, - endOptional, - beginParameter, - endParameter, - alternation, - text, - invalid, - }; - - struct Node - { - Node(NodeType type, - std::size_t start, - std::size_t end, - std::variant> children); - - [[nodiscard]] NodeType Type() const; - [[nodiscard]] std::size_t Start() const; - [[nodiscard]] std::size_t End() const; - - [[nodiscard]] std::string Text(); - [[nodiscard]] std::string Text() const; - - [[nodiscard]] std::vector& Children(); - [[nodiscard]] const std::vector& Children() const; - - [[nodiscard]] const std::variant>& GetLeafNodes() const; - - bool operator==(const Node& other) const = default; - - private: - NodeType type; - std::size_t start; - std::size_t end; - std::variant> children; - }; - - struct Token - { - Token(TokenType type, std::string text, std::size_t start, std::size_t end); - - [[nodiscard]] TokenType Type() const; - [[nodiscard]] std::string Text() const; - [[nodiscard]] std::size_t Start() const; - [[nodiscard]] std::size_t End() const; - - [[nodiscard]] static bool IsEscapeCharacter(char ch); - [[nodiscard]] static TokenType TypeOf(char ch); - [[nodiscard]] static bool CanEscape(char ch); - [[nodiscard]] static std::string NameOf(TokenType type); - [[nodiscard]] static std::string SymbolOf(TokenType type); - [[nodiscard]] static std::string PurposeOf(TokenType type); - - bool operator==(const Token& other) const = default; - - private: - TokenType type; - std::string text; - std::size_t start; - std::size_t end; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/CMakeLists.txt b/cucumber_cpp/library/cucumber_expression/CMakeLists.txt deleted file mode 100644 index c9dcd0b6..00000000 --- a/cucumber_cpp/library/cucumber_expression/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -add_library(cucumber_cpp.library.cucumber_expression ${CCR_EXCLUDE_FROM_ALL}) - -target_sources(cucumber_cpp.library.cucumber_expression PRIVATE - Argument.cpp - Argument.hpp - Ast.cpp - Ast.hpp - Errors.cpp - Errors.hpp - Expression.cpp - Expression.hpp - ExpressionParser.cpp - ExpressionParser.hpp - ExpressionTokenizer.cpp - ExpressionTokenizer.hpp - Group.cpp - Group.hpp - MatchRange.cpp - MatchRange.hpp - ParameterRegistry.cpp - ParameterRegistry.hpp - RegexStrategy.hpp - RegexStrategyFactory.cpp - RegexStrategyFactory.hpp - RegularExpression.cpp - RegularExpression.hpp - StdRegexStrategy.cpp - StdRegexStrategy.hpp - TreeRegexp.cpp - TreeRegexp.hpp -) - -target_include_directories(cucumber_cpp.library.cucumber_expression PUBLIC - ../../.. -) - -target_link_libraries(cucumber_cpp.library.cucumber_expression PUBLIC - fmt::fmt -) - -if (CCR_HAS_RE2) - target_sources(cucumber_cpp.library.cucumber_expression PRIVATE - Re2RegexStrategy.cpp - Re2RegexStrategy.hpp - ) - - target_compile_definitions(cucumber_cpp.library.cucumber_expression PRIVATE - CCR_HAS_RE2 - ) - - target_link_libraries(cucumber_cpp.library.cucumber_expression PUBLIC - re2::re2 - ) -endif() - -if (CCR_BUILD_TESTS) - add_subdirectory(test) -endif() diff --git a/cucumber_cpp/library/cucumber_expression/Errors.cpp b/cucumber_cpp/library/cucumber_expression/Errors.cpp deleted file mode 100644 index 14ece943..00000000 --- a/cucumber_cpp/library/cucumber_expression/Errors.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "fmt/format.h" -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - std::string PointAt(std::size_t column) - { - return std::string(column, ' ') + "^"; - } - - std::string PointAtLocated(const auto& node) - { - auto pointer = PointAt(node.Start()); - if (node.Start() + 1 < node.End()) - { - pointer.resize(node.End() - 1, '-'); - pointer += "^"; - } - return pointer; - } - } - - Error::Error(std::size_t column, std::string_view expression, std::string_view pointer, std::string_view problem, std::string_view solution) - : std::runtime_error{ - fmt::format( - "This Cucumber Expression has a problem at column {}:\n" - "\n" - "{}\n" - "{}\n" - "{}\n" - "{}\n", - column + 1, - expression, - pointer, - problem, - solution) - } - {} - - CantEscape::CantEscape(std::string_view expression, std::size_t column) - : Error{ - column, - expression, - PointAt(column), - R"(Only the characters '{', '}', '(', ')', '\', '/' and whitespace can be escaped)", - R"(If you did mean to use an '\' you can use '\\' to escape it)" - } - {} - - TheEndOfLineCannotBeEscaped::TheEndOfLineCannotBeEscaped(std::string_view expression) - : Error{ - expression.length(), - expression, - PointAt(expression.length()), - R"(The end of line can not be escaped)", - R"(You can use '\\' to escape the '\')" - } - {} - - AlternationNotAllowedInOptional::AlternationNotAllowedInOptional(std::string_view expression, const Token& token) - : Error{ - token.Start(), - expression, - PointAtLocated(token), - R"(An alternation can not be used inside an optional)", - R"(If you did not mean to use an alternation you can use '\/' to escape the '/'. -Otherwise rephrase your expression or consider using a regular expression instead.)", - } - {} - - InvalidParameterTypeNameInNode::InvalidParameterTypeNameInNode(std::string_view expression, const Token& token) - : Error{ - token.Start(), - expression, - PointAtLocated(token), - R"(Parameter names may not contain '{', '}', '(', ')', '\' or '/')", - R"(Did you mean to use a regular expression?)", - } - {} - - MissingEndToken::MissingEndToken(std::string_view expression, TokenType beginToken, TokenType endToken, const Token& token) - : Error{ - token.Start(), - expression, - PointAtLocated(token), - fmt::format(R"(The '{}' does not have a matching '{}')", Token::SymbolOf(beginToken), Token::SymbolOf(endToken)), - fmt::format(R"(If you did not intend to use {} you can use '\\{}' to escape the {})", Token::PurposeOf(beginToken), Token::SymbolOf(beginToken), Token::PurposeOf(beginToken)), - } - {} - - NoEligibleParsers::NoEligibleParsers(std::span tokens) - : std::runtime_error{ - fmt::format("No eligible parsers for [{}]", std::accumulate(tokens.begin() + 1, tokens.end(), Token::NameOf(tokens.begin()->Type()), - [](const auto& acc, const auto& token) -> std::string - { - return acc + ", " + Token::NameOf(token.Type()); - })), - } - {} - - OptionalMayNotBeEmpty::OptionalMayNotBeEmpty(const Node& node, std::string_view expression) - : Error{ - node.Start(), - expression, - PointAtLocated(node), - "An optional must contain some text", - R"(If you did not mean to use an optional you can use '\(' to escape the '(')", - } - {} - - ParameterIsNotAllowedInOptional::ParameterIsNotAllowedInOptional(const Node& node, std::string_view expression) - : Error{ - node.Start(), - expression, - PointAtLocated(node), - "An optional may not contain a parameter type", - R"(If you did not mean to use an parameter type you can use '\{' to escape the '{')", - } - {} - - OptionalIsNotAllowedInOptional::OptionalIsNotAllowedInOptional(const Node& node, std::string_view expression) - : Error{ - node.Start(), - expression, - PointAtLocated(node), - "An optional may not contain an other optional", - R"(If you did not mean to use an optional type you can use '\(' to escape the '('. -For more complicated expressions consider using a regular expression instead.)", - } - {} - - AlternativeMayNotExclusivelyContainOptionals::AlternativeMayNotExclusivelyContainOptionals(const Node& node, std::string_view expression) - : Error{ - node.Start(), - expression, - PointAtLocated(node), - "An alternative may not exclusively contain optionals", - R"(If you did not mean to use an optional you can use '\(' to escape the '(')", - } - {} - - AlternativeMayNotBeEmpty::AlternativeMayNotBeEmpty(const Node& node, std::string_view expression) - : Error{ - node.Start(), - expression, - PointAtLocated(node), - "Alternative may not be empty", - R"(If you did not mean to use an alternative you can use '\/' to escape the '/')", - } - {} - - UndefinedParameterTypeError::UndefinedParameterTypeError(const Node& node, std::string expression, std::string undefinedParameterName) - : Error{ - node.Start(), - expression, - PointAtLocated(node), - fmt::format(R"(Undefined parameter type '{}')", undefinedParameterName), - fmt::format(R"(Please register a ParameterType for '{}')", undefinedParameterName), - } - , expression{ std::move(expression) } - , undefinedParameterName{ std::move(undefinedParameterName) } - {} -} diff --git a/cucumber_cpp/library/cucumber_expression/Errors.hpp b/cucumber_cpp/library/cucumber_expression/Errors.hpp deleted file mode 100644 index 7ed9a217..00000000 --- a/cucumber_cpp/library/cucumber_expression/Errors.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_ERRORS_HPP -#define CUCUMBER_EXPRESSION_ERRORS_HPP - -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct Error - : std::runtime_error - { - Error(std::size_t column, std::string_view expression, std::string_view pointer, std::string_view problem, std::string_view solution); - }; - - struct CantEscape - : Error - { - CantEscape(std::string_view expression, std::size_t column); - }; - - struct TheEndOfLineCannotBeEscaped - : Error - { - explicit TheEndOfLineCannotBeEscaped(std::string_view expression); - }; - - struct AlternationNotAllowedInOptional - : Error - { - AlternationNotAllowedInOptional(std::string_view expression, const Token& token); - }; - - struct InvalidParameterTypeNameInNode - : Error - { - InvalidParameterTypeNameInNode(std::string_view expression, const Token& token); - }; - - struct MissingEndToken - : Error - { - MissingEndToken(std::string_view expression, TokenType beginToken, TokenType endToken, const Token& token); - }; - - struct NoEligibleParsers - : std::runtime_error - { - explicit NoEligibleParsers(std::span tokens); - }; - - struct CucumberExpressionError - : std::runtime_error - { - using std::runtime_error::runtime_error; - }; - - struct OptionalMayNotBeEmpty - : Error - { - OptionalMayNotBeEmpty(const Node& node, std::string_view expression); - }; - - struct ParameterIsNotAllowedInOptional - : Error - { - ParameterIsNotAllowedInOptional(const Node& node, std::string_view expression); - }; - - struct OptionalIsNotAllowedInOptional - : Error - { - OptionalIsNotAllowedInOptional(const Node& node, std::string_view expression); - }; - - struct AlternativeMayNotExclusivelyContainOptionals - : Error - { - AlternativeMayNotExclusivelyContainOptionals(const Node& node, std::string_view expression); - }; - - struct AlternativeMayNotBeEmpty - : Error - { - AlternativeMayNotBeEmpty(const Node& node, std::string_view expression); - }; - - struct UndefinedParameterTypeError - : Error - { - UndefinedParameterTypeError(const Node& node, std::string expression, std::string undefinedParameterName); - - std::string expression; - std::string undefinedParameterName; - }; - - struct InvalidTokenType - : std::logic_error - { - using std::logic_error::logic_error; - }; - - struct InvalidNodeType - : std::logic_error - { - using std::logic_error::logic_error; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/Expression.cpp b/cucumber_cpp/library/cucumber_expression/Expression.cpp deleted file mode 100644 index 4742fae1..00000000 --- a/cucumber_cpp/library/cucumber_expression/Expression.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Expression.hpp" -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "cucumber_cpp/library/cucumber_expression/ExpressionParser.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "fmt/format.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - std::string EscapeRegex(std::string_view text) - { - using namespace std::literals; - std::string escapedText{}; - escapedText.reserve(text.size() * 2); - const auto escapePattern = R"(\^[({$.|?*+})])"sv; - - for (const auto& character : text) - { - if (std::ranges::find(escapePattern, character) != escapePattern.end()) - escapedText += '\\'; - escapedText += character; - } - - return escapedText; - } - - std::string CreateEmptyRegexString(const Node& node) - { - std::string partialRegex{}; - partialRegex.reserve(node.Children().size() * 10); - return partialRegex; - } - - auto GetNodesWithType(const Node& node, NodeType type) - { - return std::views::filter(node.Children(), [type](const Node& child) - { - return child.Type() == type; - }); - } - - bool NodesAreEmpty(const Node& node) - { - return GetNodesWithType(node, NodeType::text).empty(); - } - - bool ContainsNodeWithType(const Node& node, NodeType type) - { - return !GetNodesWithType(node, type).empty(); - } - - bool ContainsNodeWithParameters(const Node& node) - { - return ContainsNodeWithType(node, NodeType::parameter); - } - - bool ContainsNodeWithOptionals(const Node& node) - { - return ContainsNodeWithType(node, NodeType::optional); - } - } - - Expression::Expression(std::string expression, ParameterRegistry& parameterRegistry) - : expression{ std::move(expression) } - , parameterRegistry{ parameterRegistry } - , pattern{ RewriteToRegex(ExpressionParser{}.Parse(this->expression)) } - , treeRegexp{ pattern } - { - } - - std::string_view Expression::Source() const - { - return expression; - } - - std::string_view Expression::Pattern() const - { - return pattern; - } - - std::optional> Expression::MatchToArguments(const std::string& text) const - { - auto group = treeRegexp.MatchToGroup(text); - if (!group.has_value()) - return std::nullopt; - - return Argument::BuildArguments(group.value(), parameters); - } - - std::string Expression::RewriteToRegex(const Node& node) - { - switch (node.Type()) - { - case NodeType::text: - return EscapeRegex(node.Text()); - case NodeType::optional: - return RewriteOptional(node); - case NodeType::alternation: - return RewriteAlternation(node); - case NodeType::alternative: - return RewriteAlternative(node); - case NodeType::parameter: - return RewriteParameter(node); - case NodeType::expression: - return RewriteExpression(node); - } - - throw InvalidNodeType{ "Invalid node type" }; - } - - std::string Expression::RewriteOptional(const Node& node) - { - std::string partialRegex{ CreateEmptyRegexString(node) }; - - if (ContainsNodeWithParameters(node)) - throw ParameterIsNotAllowedInOptional(node, expression); - - if (ContainsNodeWithOptionals(node)) - throw OptionalIsNotAllowedInOptional(node, expression); - - if (NodesAreEmpty(node)) - throw OptionalMayNotBeEmpty(node, expression); - - for (const auto& child : node.Children()) - partialRegex += RewriteToRegex(child); - - return fmt::format(R"((?:{})?)", partialRegex); - } - - std::string Expression::RewriteAlternation(const Node& node) - { - for (const auto& child : node.Children()) - { - if (child.Children().empty()) - throw AlternativeMayNotBeEmpty(node, expression); - - if (NodesAreEmpty(child)) - throw AlternativeMayNotExclusivelyContainOptionals(node, expression); - } - - std::string partialRegex{ CreateEmptyRegexString(node) }; - partialRegex += RewriteToRegex(node.Children().front()); - for (const auto& child : node.Children() | std::views::drop(1)) - partialRegex += '|' + RewriteToRegex(child); - - return fmt::format(R"((?:{}))", partialRegex); - } - - std::string Expression::RewriteAlternative(const Node& node) - { - std::string partialRegex{ CreateEmptyRegexString(node) }; - - for (const auto& child : node.Children()) - partialRegex += RewriteToRegex(child); - - return partialRegex; - } - - std::string Expression::RewriteParameter(const Node& node) - { - try - { - auto parameter = parameterRegistry.Lookup(node.Text()); - if (parameter.regex.empty()) - throw UndefinedParameterTypeError(node, expression, node.Text()); - - parameters.push_back(parameter); - - std::string partialRegex{}; - if (parameter.regex.size() == 1) - partialRegex = fmt::format(R"(({}))", parameter.regex.front()); - else - { - partialRegex = { parameter.regex.front() }; - for (const auto& parameterRegex : parameter.regex | std::views::drop(1)) - partialRegex += R"()|(?:)" + parameterRegex; - partialRegex = fmt::format(R"(((?:{})))", partialRegex); - } - return partialRegex; - } - catch (const std::out_of_range&) - { - throw UndefinedParameterTypeError(node, expression, node.Text()); - } - } - - std::string Expression::RewriteExpression(const Node& node) - { - std::string partialRegex{ CreateEmptyRegexString(node) }; - - for (const auto& child : node.Children()) - partialRegex += RewriteToRegex(child); - - return fmt::format("^{}$", partialRegex); - } -} diff --git a/cucumber_cpp/library/cucumber_expression/Expression.hpp b/cucumber_cpp/library/cucumber_expression/Expression.hpp deleted file mode 100644 index 52adec61..00000000 --- a/cucumber_cpp/library/cucumber_expression/Expression.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_EXPRESSION_HPP -#define CUCUMBER_EXPRESSION_EXPRESSION_HPP - -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp" -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct Expression - { - Expression(std::string expression, ParameterRegistry& parameterRegistry); - - [[nodiscard]] std::string_view Source() const; - [[nodiscard]] std::string_view Pattern() const; - - [[nodiscard]] std::optional> MatchToArguments(const std::string& text) const; - - private: - std::string RewriteToRegex(const Node& node); - std::string RewriteOptional(const Node& node); - std::string RewriteAlternation(const Node& node); - std::string RewriteAlternative(const Node& node); - std::string RewriteParameter(const Node& node); - std::string RewriteExpression(const Node& node); - - std::string expression; - ParameterRegistry& parameterRegistry; - std::vector parameters; - std::string pattern; - - TreeRegexp treeRegexp; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/ExpressionParser.cpp b/cucumber_cpp/library/cucumber_expression/ExpressionParser.cpp deleted file mode 100644 index e285bd08..00000000 --- a/cucumber_cpp/library/cucumber_expression/ExpressionParser.cpp +++ /dev/null @@ -1,258 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/ExpressionParser.hpp" -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - struct MatchTokenHelper - { - TokenType t; - - bool in(auto... args) - { - return (... || (args == t)); - } - }; - - auto MatchToken(TokenType expected) - { - return MatchTokenHelper{ expected }; - } - - bool LookingAt(std::span tokens, std::size_t position, TokenType type) - { - if (position > tokens.size()) - return type == TokenType::endOfLine; - - return tokens[position].Type() == type; - } - - bool LookingAtAny(std::span tokens, std::size_t position, const std::vector& types) - { - return std::ranges::any_of(types, [tokens, position](TokenType type) - { - return LookingAt(tokens, position, type); - }); - } - - std::vector CreateAlternativeNodes(std::size_t start, std::size_t end, std::span separators, std::span> alternatives) - { - std::vector retval; - - for (std::size_t index = 0, max = alternatives.size(); index < max; ++index) - { - if (index == 0) - { - const auto& rightSeparator = separators[index]; - retval.emplace_back(NodeType::alternative, start, rightSeparator.Start(), alternatives[index]); - } - else if (index == max - 1) - { - const auto& leftSeparator = separators[index - 1]; - retval.emplace_back(NodeType::alternative, leftSeparator.End(), end, alternatives[index]); - } - else - { - const auto& rightSeparator = separators[index]; - const auto& leftSeparator = separators[index - 1]; - retval.emplace_back(NodeType::alternative, leftSeparator.End(), rightSeparator.Start(), alternatives[index]); - } - } - return retval; - } - - std::vector SplitAlternatives(std::size_t start, std::size_t end, std::span alternation) - { - std::vector separators; - std::vector> alternatives{ {} }; - - for (const auto& node : alternation) - { - if (node.Type() == NodeType::alternative) - { - separators.push_back(node); - alternatives.emplace_back(); - } - else - alternatives.back().push_back(node); - } - - return CreateAlternativeNodes(start, end, separators, alternatives); - } - } - - ExpressionParser::Result ExpressionParser::SubParser::Parse(const ParserState& parserState) const - { - return this->parser(parserState, *this); - } - - Node ExpressionParser::Parse(std::string_view expression) - { - tokens = ExpressionTokenizer{}.Tokenize(expression); - - ExpressionParser::SubParser parseText = { .parser = [](const ParserState& parserState, const ExpressionParser::SubParser& /* subParser */) -> ExpressionParser::Result - { - const auto& token = parserState.tokens[parserState.current]; - if (MatchToken(token.Type()).in(TokenType::whiteSpace, TokenType::text, TokenType::endParameter, TokenType::endOptional)) - return { .consumed = 1, .node = Node{ - NodeType::text, - token.Start(), - token.End(), - token.Text(), - } }; - - if (token.Type() == TokenType::alternation) - throw AlternationNotAllowedInOptional{ parserState.expression, token }; - - return { .consumed = 0, .node = std::nullopt }; - } }; - - ExpressionParser::SubParser parseName = { .parser = [](const ParserState& parserState, const ExpressionParser::SubParser& /* subParser */) -> ExpressionParser::Result - { - const auto& token = parserState.tokens[parserState.current]; - if (MatchToken(token.Type()).in(TokenType::whiteSpace, TokenType::text)) - return { .consumed = 1, .node = Node{ - NodeType::text, - token.Start(), - token.End(), - token.Text(), - } }; - - if (MatchToken(token.Type()).in(TokenType::beginParameter, TokenType::endParameter, TokenType::beginOptional, TokenType::endOptional, TokenType::alternation)) - throw InvalidParameterTypeNameInNode{ parserState.expression, token }; - - return { .consumed = 0, .node = std::nullopt }; - } }; - - auto parseParameter = ParseBetweenGenerator(NodeType::parameter, TokenType::beginParameter, TokenType::endParameter); - parseParameter.subParsers = { parseName }; - - auto parseOptional = ParseBetweenGenerator(NodeType::optional, TokenType::beginOptional, TokenType::endOptional); - parseOptional.subParsers = { parseOptional, parseParameter, parseText }; - - ExpressionParser::SubParser parseAlternativeSeparator = { .parser = [this](const ParserState& parserState, const SubParser& /* subParser */) -> Result - { - if (!LookingAt(tokens, parserState.current, TokenType::alternation)) - return { .consumed = 0, .node = std::nullopt }; - - auto token = tokens[parserState.current]; - return { .consumed = 1, .node = std::optional{ - std::in_place_t{}, - NodeType::alternative, - token.Start(), - token.End(), - token.Text(), - } }; - } }; - - ExpressionParser::SubParser parseAlternation = { .parser = [this](const ParserState& parserState, const SubParser& subParser) -> ExpressionParser::Result - { - auto previous = parserState.current - 1; - if (!LookingAtAny(tokens, previous, { TokenType::startOfLine, TokenType::whiteSpace, TokenType::endParameter })) - return { .consumed = 0, .node = std::nullopt }; - - auto [consumed, ast] = ParseTokensUntil(parserState.expression, subParser.subParsers, parserState.current, { TokenType::whiteSpace, TokenType::endOfLine, TokenType::beginParameter }); - auto subCurrent = parserState.current + consumed; - if (std::ranges::none_of(ast, [](const auto& node) - { - return node.Type() == NodeType::alternative; - })) - return { .consumed = 0, .node = std::nullopt }; - - auto start = tokens[parserState.current].Start(); - auto end = tokens[subCurrent].Start(); - - return { .consumed = consumed, .node = Node{ - NodeType::alternation, - start, - end, - SplitAlternatives(start, end, ast), - } }; - }, - .subParsers = { - parseAlternativeSeparator, - parseOptional, - parseParameter, - parseText, - } }; - - auto parseCucumberExpression = ParseBetweenGenerator(NodeType::expression, TokenType::startOfLine, TokenType::endOfLine); - parseCucumberExpression.subParsers = { parseAlternation, parseOptional, parseParameter, parseText }; - - auto [opt, ast] = parseCucumberExpression.Parse(ParserState{ .expression = expression, .tokens = tokens, .current = 0 }); - return *ast; - } - - ExpressionParser::SubParser ExpressionParser::ParseBetweenGenerator(NodeType type, TokenType beginToken, TokenType endToken) const - { - return { .parser = [this, type, beginToken, endToken](const ParserState& parserState, const SubParser& subParser) -> Result - { - if (!LookingAt(parserState.tokens, parserState.current, beginToken)) - return { .consumed = 0, .node = std::nullopt }; - - auto subCurrent = parserState.current + 1; - auto [consumed, ast] = ParseTokensUntil(parserState.expression, subParser.subParsers, subCurrent, { endToken, TokenType::endOfLine }); - subCurrent += consumed; - - // endToken not found - if (!LookingAt(parserState.tokens, subCurrent, endToken)) - throw MissingEndToken{ parserState.expression, beginToken, endToken, parserState.tokens[parserState.current] }; - - // consumed endToken - auto start = parserState.tokens[parserState.current].Start(); - auto end = parserState.tokens[subCurrent].End(); - consumed = subCurrent + 1 - parserState.current; - return { .consumed = consumed, .node = Node{ - type, - start, - end, - ast, - } }; - } }; - } - - std::tuple> ExpressionParser::ParseTokensUntil(std::string_view expression, std::span> parsers, std::size_t startAt, const std::vector& endTokens) const - { - auto current = startAt; - auto size = tokens.size(); - std::vector ast; - - while (current < size) - { - if (LookingAtAny(tokens, current, endTokens)) - break; - - auto [consumed, node] = ParseToken(expression, parsers, current); - if (consumed == 0) - throw NoEligibleParsers{ tokens }; - - current += consumed; - ast.emplace_back(*node); - } - return { current - startAt, ast }; - } - - ExpressionParser::Result ExpressionParser::ParseToken(std::string_view expression, std::span> parsers, std::size_t startAt) const - { - for (const auto& parser : parsers) - { - auto [consumed, ast] = parser.get().Parse(ParserState{ .expression = expression, .tokens = tokens, .current = startAt }); - if (consumed > 0) - return { .consumed = consumed, .node = ast }; - } - - throw NoEligibleParsers{ tokens }; - } -} diff --git a/cucumber_cpp/library/cucumber_expression/ExpressionParser.hpp b/cucumber_cpp/library/cucumber_expression/ExpressionParser.hpp deleted file mode 100644 index 10ccbdb6..00000000 --- a/cucumber_cpp/library/cucumber_expression/ExpressionParser.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_EXPRESSIONPARSER_HPP -#define CUCUMBER_EXPRESSION_EXPRESSIONPARSER_HPP - -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct ExpressionParser - { - Node Parse(std::string_view expression); - - struct Result - { - std::size_t consumed{}; - std::optional node; - }; - - struct ParserState - { - std::string_view expression; - std::span tokens; - std::size_t current; - }; - - struct SubParser - { - std::function parser; - std::vector> subParsers; - - [[nodiscard]] Result Parse(const ParserState& parser) const; - }; - - private: - using Parsers = std::vector>; - using Tokens = std::vector; - - [[nodiscard]] SubParser ParseBetweenGenerator(NodeType type, TokenType beginToken, TokenType endToken) const; - - [[nodiscard]] std::tuple> ParseTokensUntil(std::string_view expression, std::span> parsers, std::size_t startAt, const std::vector& endTokens) const; - [[nodiscard]] Result ParseToken(std::string_view expression, std::span> parsers, std::size_t startAt) const; - - Tokens tokens; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.cpp b/cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.cpp deleted file mode 100644 index b5b9aa93..00000000 --- a/cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.hpp" -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - std::vector ExpressionTokenizer::Tokenize(std::string_view expressionToTokenize) - { - expression = expressionToTokenize; - - std::vector tokens; - auto previousTokenType = TokenType::startOfLine; - auto treatAsText = false; - - if (expression.empty()) - tokens.emplace_back(TokenType::startOfLine, "", 0, 0); - - for (const auto ch : expression) - { - if (Token::IsEscapeCharacter(ch) && !treatAsText) - { - ++escapedCharacters; - treatAsText = true; - continue; - } - - auto currentTokenType = TokenTypeOf(ch, treatAsText); - treatAsText = false; - - if (ShouldCreateNewToken(previousTokenType, currentTokenType)) - tokens.push_back(CreateToken(previousTokenType)); - - previousTokenType = currentTokenType; - buffer += ch; - } - - if (!buffer.empty()) - tokens.push_back(CreateToken(previousTokenType)); - - if (treatAsText) - throw TheEndOfLineCannotBeEscaped(expression); - - tokens.emplace_back(TokenType::endOfLine, "", expression.length(), expression.length()); - - return tokens; - } - - [[nodiscard]] TokenType ExpressionTokenizer::TokenTypeOf(char ch, bool treatAsText) const - { - if (!treatAsText) - return Token::TypeOf(ch); - - if (Token::CanEscape(ch)) - return TokenType::text; - - throw CantEscape(expression, startIndex + buffer.length() + escapedCharacters); - } - - [[nodiscard]] bool ExpressionTokenizer::ShouldCreateNewToken(TokenType previousTokenType, TokenType currentTokenType) - { - return previousTokenType != currentTokenType || (currentTokenType != TokenType::whiteSpace && currentTokenType != TokenType::text); - } - - [[nodiscard]] Token ExpressionTokenizer::CreateToken(TokenType type) - { - std::size_t escapedCount = 0; - - if (type == TokenType::text) - escapedCount = std::exchange(this->escapedCharacters, 0); - - auto start = startIndex; - auto end = startIndex + buffer.length() + escapedCount; - auto text = std::string{}; - std::swap(text, buffer); - startIndex = end; - - return { type, text, start, end }; - } -} diff --git a/cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.hpp b/cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.hpp deleted file mode 100644 index 960b712c..00000000 --- a/cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_EXPRESSIONTOKENIZER_HPP -#define CUCUMBER_EXPRESSION_EXPRESSIONTOKENIZER_HPP - -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct ExpressionTokenizer - { - std::vector Tokenize(std::string_view expressionToTokenize); - - private: - [[nodiscard]] TokenType TokenTypeOf(char ch, bool treatAsText) const; - [[nodiscard]] static bool ShouldCreateNewToken(TokenType previousTokenType, TokenType currentTokenType); - [[nodiscard]] Token CreateToken(TokenType type); - - std::string_view expression; - - std::size_t escapedCharacters{ 0 }; - std::string buffer; - - std::size_t startIndex{ 0 }; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/Group.cpp b/cucumber_cpp/library/cucumber_expression/Group.cpp deleted file mode 100644 index 2299c818..00000000 --- a/cucumber_cpp/library/cucumber_expression/Group.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - std::optional ToString(const ArgumentGroup& group) - { - return group.value; - } - } - - std::vector> ArgumentGroup::Values() const - { - if (children.empty()) - return { value }; - - auto strings = children | std::views::transform(ToString); - - return { strings.begin(), strings.end() }; - } -} diff --git a/cucumber_cpp/library/cucumber_expression/Group.hpp b/cucumber_cpp/library/cucumber_expression/Group.hpp deleted file mode 100644 index ab94d9d4..00000000 --- a/cucumber_cpp/library/cucumber_expression/Group.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_GROUP_HPP -#define CUCUMBER_EXPRESSION_GROUP_HPP - -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct ArgumentGroup - { - std::optional value; - std::optional start; - std::optional end; - - std::vector children; - - [[nodiscard]] std::vector> Values() const; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/MatchRange.cpp b/cucumber_cpp/library/cucumber_expression/MatchRange.cpp deleted file mode 100644 index 8083ec7b..00000000 --- a/cucumber_cpp/library/cucumber_expression/MatchRange.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/MatchRange.hpp" -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - - std::smatch::const_iterator MatchRange::begin() const - { - return first; - } - - std::smatch::const_iterator MatchRange::end() const - { - return second; - } - - const std::ssub_match& MatchRange::operator[](diff_t index) const - { - return *std::next(begin(), index); - } -} diff --git a/cucumber_cpp/library/cucumber_expression/MatchRange.hpp b/cucumber_cpp/library/cucumber_expression/MatchRange.hpp deleted file mode 100644 index c95c9860..00000000 --- a/cucumber_cpp/library/cucumber_expression/MatchRange.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_MATCH_RANGE_HPP -#define CUCUMBER_EXPRESSION_MATCH_RANGE_HPP - -// IWYU pragma: private, include "cucumber_cpp/Steps.hpp" -// IWYU pragma: friend cucumber_cpp/.* - -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct MatchRange : std::pair - { - using std::pair::pair; - using diff_t = std::smatch::const_iterator::difference_type; - - [[nodiscard]] std::smatch::const_iterator - begin() const; - [[nodiscard]] std::smatch::const_iterator end() const; - - [[nodiscard]] const std::ssub_match& operator[](diff_t index) const; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/Matcher.hpp b/cucumber_cpp/library/cucumber_expression/Matcher.hpp deleted file mode 100644 index 2a98c6ba..00000000 --- a/cucumber_cpp/library/cucumber_expression/Matcher.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_MATCHER_HPP -#define CUCUMBER_EXPRESSION_MATCHER_HPP - -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Expression.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegularExpression.hpp" -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - using Matcher = std::variant; - - struct SourceVisitor - { - std::string_view operator()(const auto& expression) const - { - return expression.Source(); - } - }; - - struct PatternVisitor - { - std::string_view operator()(const auto& expression) const - { - return expression.Pattern(); - } - }; - - struct MatchVisitor - { - std::optional> operator()(const auto& expression) const - { - return expression.MatchToArguments(text); - } - - const std::string& text; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/ParameterRegistry.cpp b/cucumber_cpp/library/cucumber_expression/ParameterRegistry.cpp deleted file mode 100644 index f163dc86..00000000 --- a/cucumber_cpp/library/cucumber_expression/ParameterRegistry.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "fmt/format.h" -#include "fmt/ranges.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - template - std::function(const ConvertFunctionArg&)> CreateStreamConverter() - { - return [](const ConvertFunctionArg& matches) -> std::optional - { - if (matches[0].has_value()) - return StringTo(matches[0].value()); - - return std::nullopt; - }; - }; - - std::function(const ConvertFunctionArg&)> CreateStringConverter() - { - return [](const ConvertFunctionArg& matches) -> std::optional - { - std::string str = matches[0].has_value() - ? matches[0].value() - : matches[1].value(); - - str = std::regex_replace(str, std::regex(R"__(\\")__"), "\""); - str = std::regex_replace(str, std::regex(R"__(\\')__"), "'"); - - return str; - }; - } - - std::function(const ConvertFunctionArg&)> CreateByteConverter() - { - return [](const ConvertFunctionArg& matches) -> std::optional - { - if (matches[0].has_value()) - return static_cast(StringTo(matches[0].value())); - - return std::nullopt; - }; - } - - bool SortMappedByRegex(const ParameterType* lhs, const ParameterType* rhs) - { - if (lhs->preferForRegexMatch && !rhs->preferForRegexMatch) - return true; - - if (!lhs->preferForRegexMatch && rhs->preferForRegexMatch) - return false; - - return lhs->name < rhs->name; - } - } - - std::strong_ordering CustomParameterEntry::operator<=>(const CustomParameterEntry& other) const - { - return std::tie(params.name, params.regex) <=> std::tie(other.params.name, other.params.regex); - } - - ParameterRegistry::ParameterRegistry(const std::set>& customParameters) - { - const static std::string integerNegativeRegex{ R"__(-?\d+)__" }; - const static std::string integerPositiveRegex{ R"__(\d+)__" }; - const static std::string floatRegex{ R"__([-+]?(?:\d+(?:\.\d+)?|\.\d+)(?:[Ee][+-]?\d+)?)__" }; - const static std::string stringRegex{ R"__("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)')__" }; - const static std::string wordRegex{ R"__([^\s]+)__" }; - const static std::string anonymousRegex{ R"__(.*)__" }; - - AddBuiltinParameter("int", { integerNegativeRegex, integerPositiveRegex }, CreateStreamConverter(), true); - AddBuiltinParameter("float", { floatRegex }, CreateStreamConverter()); - AddBuiltinParameter("word", { wordRegex }, CreateStreamConverter()); - AddBuiltinParameter("string", { stringRegex }, CreateStringConverter()); - AddBuiltinParameter("", { anonymousRegex }, CreateStreamConverter()); - AddBuiltinParameter("bigdecimal", { floatRegex }, CreateStreamConverter()); - AddBuiltinParameter("biginteger", { { integerNegativeRegex, integerPositiveRegex } }, CreateStreamConverter()); - AddBuiltinParameter("byte", { { integerNegativeRegex, integerPositiveRegex } }, CreateByteConverter()); - AddBuiltinParameter("short", { { integerNegativeRegex, integerPositiveRegex } }, CreateStreamConverter()); - AddBuiltinParameter("long", { { integerNegativeRegex, integerPositiveRegex } }, CreateStreamConverter()); - AddBuiltinParameter("double", { floatRegex }, CreateStreamConverter()); - - // extension - AddBuiltinParameter("bool", { wordRegex }, CreateStreamConverter()); - - for (const auto& parameter : customParameters) - AddParameter(ParameterType{ .name = parameter.params.name, .regex = { std::string(parameter.params.regex) }, .isBuiltin = false, .useForSnippets = parameter.params.useForSnippets, .location = parameter.location }); - } - - const std::map>& ParameterRegistry::GetParameters() const - { - return parameterTypesByName; - } - - const ParameterType& ParameterRegistry::Lookup(const std::string& name) const - { - return parameterTypesByName.at(name); - } - - const ParameterType* ParameterRegistry::LookupByRegexp(const std::string& regex) const - { - if (!parameterTypesByRegex.contains(regex)) - return nullptr; - - const auto& parameters = parameterTypesByRegex.at(regex); - - if (parameters.size() == 0) - return nullptr; - - if (parameters.size() > 1 && !parameters[0]->preferForRegexMatch) - throw CucumberExpressionError{ fmt::format("There are multiple parameter types but none are prefered for the regexp \"{}\": {}", - regex, - fmt::join(parameters | std::views::transform([](const ParameterType* parameter) - { - return parameter->name; - }), - ", ")) }; - - return parameters[0]; - } - - void ParameterRegistry::AssertParameterIsUnique(const std::string& name) const - { - if (parameterTypesByName.contains(name)) - { - if (name.empty()) - throw CucumberExpressionError{ "The anonymous parameter type has already been defined" }; - else - throw CucumberExpressionError{ fmt::format("There is already a parameter with name {}", name) }; - } - } - - void ParameterRegistry::AddParameter(ParameterType parameter) - { - AssertParameterIsUnique(parameter.name); - - const auto& [iter, _] = parameterTypesByName.try_emplace(parameter.name, parameter); - const auto& [key, value] = *iter; - - for (const auto& regex : parameter.regex) - { - auto& existingParametersByRegex = parameterTypesByRegex[regex]; - if (existingParametersByRegex.size() > 0 && existingParametersByRegex[0]->preferForRegexMatch && parameter.preferForRegexMatch) - throw CucumberExpressionError{ fmt::format("There can only be one preferential parameter type per regexp.\nThe regexp \"{}\" is used for two preferential parameter types, {} and {}", regex, existingParametersByRegex[0]->name, parameter.name) }; - - existingParametersByRegex.push_back(&value); - std::ranges::sort(existingParametersByRegex, SortMappedByRegex); - } - } -} diff --git a/cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp b/cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp deleted file mode 100644 index b225dcae..00000000 --- a/cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp +++ /dev/null @@ -1,310 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_PARAMETERREGISTRY_HPP -#define CUCUMBER_EXPRESSION_PARAMETERREGISTRY_HPP - -#include "fmt/format.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - using namespace std::literals; - - struct CustomParameterEntryParams - { - std::string name; - std::string regex; - bool useForSnippets; - }; - - struct CustomParameterEntry - { - CustomParameterEntryParams params; - - std::size_t localId{}; - - std::source_location location; - - std::strong_ordering operator<=>(const CustomParameterEntry& other) const; - }; - - struct ConversionError : std::runtime_error - { - using std::runtime_error::runtime_error; - }; - - template - inline To StringTo(const std::string& s) - { - if (s.empty()) - return {}; - - std::istringstream stream{ s }; - To to{}; - stream >> to; - if (stream.fail()) - throw ConversionError{ fmt::format("Cannot convert parameter {} in to {}", s, typeid(To).name()) }; - - return to; - } - - template<> - inline std::string StringTo(const std::string& s) - { - return s; - } - - template<> - inline int32_t StringTo(const std::string& s) - { - return std::stoi(s); - } - - template<> - inline int64_t StringTo(const std::string& s) - { - return std::stoll(s); - } - - template<> - inline float StringTo(const std::string& s) - { - return std::stof(s); - } - - template<> - inline double StringTo(const std::string& s) - { - return std::stod(s); - } - - namespace details - { - inline bool ichar_equals(char a, char b) - { - return std::tolower(static_cast(a)) == - std::tolower(static_cast(b)); - } - - inline bool iequals(std::string_view lhs, std::string_view rhs) - { - return std::ranges::equal(lhs, rhs, ichar_equals); - } - } - - template<> - inline bool StringTo(const std::string& s) - { - using details::iequals; - - return iequals(s, "true") || iequals(s, "1") || iequals(s, "yes") || iequals(s, "on") || iequals(s, "enabled") || iequals(s, "active"); - } - - struct ParameterType - { - std::string name; - std::vector regex; - bool isBuiltin{ false }; - bool useForSnippets{ false }; - bool preferForRegexMatch{ false }; - std::source_location location; - }; - - using ConvertFunctionArg = std::vector>; - - template - using ConverterFunction = std::function; - - // Type-erased converter used at the host/plugin ABI boundary. The stored - // std::function returns std::any so a single non-templated map can hold - // converters for all return types. std::any_cast is performed at the call - // site (host or plugin), while the underlying storage can be shared. - using AnyConverterFunction = std::function; - - using ConverterMap = std::map>; - - // Central converter registry. Each DLL has its own default map, but the - // active pointer can be redirected to a shared (host-owned) map so - // registrations and lookups cross DLL boundaries safely. - struct ConverterRegistry - { - static ConverterMap& Instance() - { - return *ActivePtr(); - } - - static ConverterMap& LocalInstance() - { - static ConverterMap map; - return map; - } - - // Redirect this DLL's active converter map to an externally owned map - // (typically the host's). Passing nullptr restores the local map. - static void SetInstance(ConverterMap* external) - { - ActivePtr() = external != nullptr ? external : &LocalInstance(); - } - - static void TakeSnapshot() - { - Snapshot() = Instance(); - } - - static void RestoreSnapshot() - { - Instance() = Snapshot(); - } - - private: - static ConverterMap*& ActivePtr() - { - static ConverterMap* ptr = &LocalInstance(); - return ptr; - } - - static ConverterMap& Snapshot() - { - static ConverterMap snapshot; - return snapshot; - } - }; - - // Backwards-compatible typed accessor. Wraps/unwraps std::any so existing - // typed callers keep working, but storage is unified in ConverterRegistry. - template - struct ConverterTypeMap - { - // Proxy allowing typed insertion/lookup against the underlying any map. - struct Proxy - { - ConverterMap& map; - - void Emplace(const std::string& name, ConverterFunction fn) - { - map[name] = [fn = std::move(fn)](const ConvertFunctionArg& args) - { - return std::any{ fn(args) }; - }; - } - - struct TypedAccessor - { - AnyConverterFunction& fn; - - T operator()(const ConvertFunctionArg& args) const - { - return std::any_cast(fn(args)); - } - }; - - TypedAccessor At(const std::string& name) - { - return TypedAccessor{ map.at(name) }; - } - - struct Assigner - { - ConverterMap& map; - std::string name; - - Assigner& operator=(ConverterFunction fn) - { - map[name] = [fn = std::move(fn)](const ConvertFunctionArg& args) - { - return std::any{ fn(args) }; - }; - return *this; - } - }; - - Assigner operator[](const std::string& name) - { - return Assigner{ map, name }; - } - }; - - static Proxy Instance() - { - return Proxy{ ConverterRegistry::Instance() }; - } - }; -} - -namespace cucumber_cpp::library::plugin -{ - struct ParameterLoader; -} - -namespace cucumber_cpp::library::cucumber_expression -{ - struct ParameterRegistry - { - explicit ParameterRegistry(const std::set>& customParameters); - - virtual ~ParameterRegistry() = default; - - [[nodiscard]] const std::map>& GetParameters() const; - - [[nodiscard]] const ParameterType& Lookup(const std::string& name) const; - [[nodiscard]] const ParameterType* LookupByRegexp(const std::string& regex) const; - - template - void AddParameter(std::string name, std::vector regex, ConverterFunction converter, std::source_location location = std::source_location::current()); - - private: - friend struct plugin::ParameterLoader; - - void AssertParameterIsUnique(const std::string& name) const; - - void AddParameter(ParameterType parameter); - - template - void AddBuiltinParameter(std::string name, std::vector regex, ConverterFunction converter, bool preferForRegexMatch = false, std::source_location location = std::source_location::current()); - - template - void AddParameter(ParameterType parameter, ConverterFunction converter); - - std::map> parameterTypesByName; - std::map, std::less<>> parameterTypesByRegex; - }; - - template - void ParameterRegistry::AddParameter(std::string name, std::vector regex, ConverterFunction converter, std::source_location location) - { - AddParameter(ParameterType{ .name = std::move(name), .regex = std::move(regex), .isBuiltin = false, .useForSnippets = false, .location = location }, converter); - } - - template - void ParameterRegistry::AddBuiltinParameter(std::string name, std::vector regex, ConverterFunction converter, bool preferForRegexMatch, std::source_location location) - { - AddParameter(ParameterType{ .name = std::move(name), .regex = std::move(regex), .isBuiltin = true, .useForSnippets = false, .preferForRegexMatch = preferForRegexMatch, .location = location }, converter); - } - - template - void ParameterRegistry::AddParameter(ParameterType parameter, ConverterFunction converter) - { - AssertParameterIsUnique(parameter.name); - - AddParameter(parameter); - - ConverterTypeMap::Instance().Emplace(parameter.name, converter); - } -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.cpp b/cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.cpp deleted file mode 100644 index a91318ef..00000000 --- a/cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp" -#include "absl/strings/string_view.h" -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - Re2RegexStrategy::Re2RegexStrategy(std::string_view pattern) - : re2{ absl::string_view{ pattern.data(), pattern.size() } } - { - if (!re2.ok()) - throw std::invalid_argument(re2.error()); - } - - std::optional Re2RegexStrategy::Match(std::string_view text) const - { - const int nCaptures = re2.NumberOfCapturingGroups(); - const int nSubmatch = nCaptures + 1; - std::vector submatch(static_cast(nSubmatch)); - - if (!re2.Match(text, 0, static_cast(text.size()), RE2::UNANCHORED, submatch.data(), nSubmatch)) - return std::nullopt; - - Matches result; - result.reserve(static_cast(nSubmatch)); - for (const auto& piece : submatch) - { - if (piece.data() == nullptr) - result.emplace_back(std::nullopt); - else - { - const auto start = static_cast(piece.data() - text.data()); - result.emplace_back(MatchGroup{ - .value = std::string(piece), - .start = start, - .end = start + piece.size(), - }); - } - } - return result; - } -} diff --git a/cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp b/cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp deleted file mode 100644 index 1b4c800a..00000000 --- a/cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_RE2REGEXSTRATEGY_HPP -#define CUCUMBER_EXPRESSION_RE2REGEXSTRATEGY_HPP - -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include "re2/re2.h" -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct Re2RegexStrategy : RegexStrategy - { - explicit Re2RegexStrategy(std::string_view pattern); - - [[nodiscard]] std::optional Match(std::string_view text) const override; - - private: - re2::RE2 re2; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp b/cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp deleted file mode 100644 index 2c1f74a5..00000000 --- a/cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_REGEXSTRATEGY_HPP -#define CUCUMBER_EXPRESSION_REGEXSTRATEGY_HPP - -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct MatchGroup - { - std::string value; - std::size_t start; - std::size_t end; - }; - - using Matches = std::vector>; - - struct RegexStrategy - { - RegexStrategy() = default; - virtual ~RegexStrategy() = default; - - [[nodiscard]] virtual std::optional Match(std::string_view text) const = 0; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.cpp b/cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.cpp deleted file mode 100644 index fcf3baf0..00000000 --- a/cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include "cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp" - -#ifdef CCR_HAS_RE2 -#include "cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp" -#include -#endif - -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - std::unique_ptr CreateRegexStrategy(std::string_view pattern) - { -#ifdef CCR_HAS_RE2 - try - { - return std::make_unique(pattern); - } - catch (const std::invalid_argument&) - { - return std::make_unique(pattern); - } -#else - return std::make_unique(pattern); -#endif - } -} diff --git a/cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.hpp b/cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.hpp deleted file mode 100644 index ab0d7e73..00000000 --- a/cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_REGEXSTRATEGYFACTORY_HPP -#define CUCUMBER_EXPRESSION_REGEXSTRATEGYFACTORY_HPP - -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - [[nodiscard]] std::unique_ptr CreateRegexStrategy(std::string_view pattern); -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/RegularExpression.cpp b/cucumber_cpp/library/cucumber_expression/RegularExpression.cpp deleted file mode 100644 index 6402ac8c..00000000 --- a/cucumber_cpp/library/cucumber_expression/RegularExpression.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/RegularExpression.hpp" -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp" -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - RegularExpression::RegularExpression(std::string expression, const ParameterRegistry& parameterRegistry) - : expression{ std::move(expression) } - , treeRegexp{ this->expression } - { - for (const auto& groupBuilder : treeRegexp.RootBuilder().Children()) - { - const auto* parameterByRegexp = parameterRegistry.LookupByRegexp(std::string{ groupBuilder.Pattern() }); - if (parameterByRegexp != nullptr) - parameters.emplace_back(*parameterByRegexp); - else - { - - parameters.emplace_back( - std::string{ "" }, - std::vector{ std::string{ groupBuilder.Pattern() } }, - false, - false, - false, - std::source_location::current()); - } - } - } - - std::string_view RegularExpression::Source() const - { - return expression; - } - - std::string_view RegularExpression::Pattern() const - { - return expression; - } - - std::optional> RegularExpression::MatchToArguments(const std::string& text) const - { - auto group = treeRegexp.MatchToGroup(text); - if (!group.has_value()) - return std::nullopt; - - return Argument::BuildArguments(group.value(), parameters); - } -} diff --git a/cucumber_cpp/library/cucumber_expression/RegularExpression.hpp b/cucumber_cpp/library/cucumber_expression/RegularExpression.hpp deleted file mode 100644 index 7c34de4e..00000000 --- a/cucumber_cpp/library/cucumber_expression/RegularExpression.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_REGULAREXPRESSION_HPP -#define CUCUMBER_EXPRESSION_REGULAREXPRESSION_HPP - -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp" -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct RegularExpression - { - explicit RegularExpression(std::string expression, const ParameterRegistry& parameterRegistry); - - [[nodiscard]] std::string_view Source() const; - [[nodiscard]] std::string_view Pattern() const; - - [[nodiscard]] std::optional> MatchToArguments(const std::string& text) const; - - private: - std::string expression; - - TreeRegexp treeRegexp; - std::vector parameters; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/StdRegexStrategy.cpp b/cucumber_cpp/library/cucumber_expression/StdRegexStrategy.cpp deleted file mode 100644 index ecd527e8..00000000 --- a/cucumber_cpp/library/cucumber_expression/StdRegexStrategy.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - StdRegexStrategy::StdRegexStrategy(std::string_view pattern) - : regex{ std::string(pattern) } - { - } - - std::optional StdRegexStrategy::Match(std::string_view text) const - { - std::smatch match; - const std::string textStr(text); - if (!std::regex_search(textStr, match, regex)) - return std::nullopt; - - Matches result; - result.reserve(match.size()); - for (std::smatch::size_type i = 0; i < match.size(); ++i) - { - if (const auto& m = match[i]; !m.matched) - result.emplace_back(std::nullopt); - else - { - const auto start = static_cast(match.position(i)); - result.emplace_back(MatchGroup{ - .value = m.str(), - .start = start, - .end = start + static_cast(m.length()), - }); - } - } - return result; - } -} diff --git a/cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp b/cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp deleted file mode 100644 index 4bea05ff..00000000 --- a/cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_STDREGEXSTRATEGY_HPP -#define CUCUMBER_EXPRESSION_STDREGEXSTRATEGY_HPP - -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct StdRegexStrategy : RegexStrategy - { - explicit StdRegexStrategy(std::string_view pattern); - - [[nodiscard]] std::optional Match(std::string_view text) const override; - - private: - std::regex regex; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/TreeRegexp.cpp b/cucumber_cpp/library/cucumber_expression/TreeRegexp.cpp deleted file mode 100644 index c612dbd8..00000000 --- a/cucumber_cpp/library/cucumber_expression/TreeRegexp.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp" -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - bool IsNonCapturing(std::string_view pattern, std::size_t pos) - { - if (pattern[pos + 1] != '?') - return false; - if (pattern[pos + 2] != '<') - return true; - - return pattern[pos + 3] == '=' || pattern[pos + 3] == '!'; - } - - void StartGroup(std::deque& stack, std::deque& groupStartStack, std::string_view pattern, std::size_t patternIndex) - { - groupStartStack.emplace_back(patternIndex); - auto& groupBuilder = stack.emplace_back(); - if (IsNonCapturing(pattern, patternIndex)) - groupBuilder.SetNonCapturing(); - } - - void FinalizeGroup(std::deque& stack, std::deque& groupStartStack, std::string_view pattern, std::size_t patternIndex) - { - if (stack.empty()) - throw std::runtime_error("Empty stack"); - - auto groupBuilder = stack.back(); - stack.pop_back(); - - auto groupStart = groupStartStack.empty() ? 0 : groupStartStack.back(); - groupStart += 1; - - if (!groupStartStack.empty()) - groupStartStack.pop_back(); - - if (groupBuilder.IsCapturing()) - { - groupBuilder.SetPattern(pattern.substr(groupStart, patternIndex - groupStart)); - stack.back().Add(groupBuilder); - } - else - groupBuilder.MoveChildrenTo(stack.back()); - } - - struct PatternGroupParser - { - enum class State : std::uint8_t - { - nonGroup, - groupStart, - groupClose - }; - - State Parse(char c) - { - State state{}; - - if (c == '[' && !escaping) - charClass = true; - else if (c == ']' && !escaping) - charClass = false; - else if (c == '(' && !escaping && !charClass) - state = State::groupStart; - else if (c == ')' && !escaping && !charClass) - state = State::groupClose; - - escaping = (c == '\\' && !escaping); - - return state; - } - - private: - bool escaping{ false }; - bool charClass{ false }; - }; - - GroupBuilder CreateGroupBuilder(std::string_view pattern) - { - std::deque stack; - std::deque groupStartStack; - PatternGroupParser patternParser; - - stack.emplace_back(); - - for (std::size_t i = 0; i < pattern.size(); ++i) - { - const char c = pattern[i]; - - switch (patternParser.Parse(c)) - { - case PatternGroupParser::State::groupStart: - StartGroup(stack, groupStartStack, pattern, i); - break; - - case PatternGroupParser::State::groupClose: - FinalizeGroup(stack, groupStartStack, pattern, i); - break; - - case PatternGroupParser::State::nonGroup: - break; - } - } - - if (stack.empty()) - throw std::runtime_error("Empty stack"); - - return stack.back(); - } - } - - void GroupBuilder::Add(GroupBuilder groupBuilder) - { - children.push_back(std::move(groupBuilder)); - } - - void GroupBuilder::SetNonCapturing() - { - capturing = false; - } - - bool GroupBuilder::IsCapturing() const - { - return capturing; - } - - void GroupBuilder::SetPattern(std::string_view pattern) - { - this->pattern = pattern; - } - - void GroupBuilder::MoveChildrenTo(GroupBuilder& target) - { - for (auto& child : children) - target.Add(std::move(child)); - children.clear(); - } - - const std::list& GroupBuilder::Children() const - { - return children; - } - - std::string_view GroupBuilder::Pattern() const - { - return pattern; - } - - ArgumentGroup GroupBuilder::Build(std::span> match, std::size_t& index) const - { - const auto groupIndex = index++; - const auto& matchGroupOpt = match[groupIndex]; - - const auto children = this->children | std::views::transform([&match, &index](const auto& child) - { - return child.Build(match, index); - }); - - return { - .value = matchGroupOpt ? std::make_optional(matchGroupOpt->value) : std::nullopt, - .start = matchGroupOpt ? std::make_optional(matchGroupOpt->start) : std::nullopt, - .end = matchGroupOpt ? std::make_optional(matchGroupOpt->end) : std::nullopt, - .children = std::vector(children.begin(), children.end()), - }; - } - - TreeRegexp::TreeRegexp(std::string_view pattern) - : storedPattern{ pattern } - , rootGroupBuilder{ CreateGroupBuilder(storedPattern) } - , regexStrategy{ CreateRegexStrategy(storedPattern) } - { - } - - TreeRegexp::TreeRegexp(const TreeRegexp& other) - : storedPattern{ other.storedPattern } - , rootGroupBuilder{ other.rootGroupBuilder } - , regexStrategy{ CreateRegexStrategy(storedPattern) } - { - } - - TreeRegexp& TreeRegexp::operator=(const TreeRegexp& other) - { - if (this != &other) - { - storedPattern = other.storedPattern; - rootGroupBuilder = other.rootGroupBuilder; - regexStrategy = CreateRegexStrategy(storedPattern); - } - return *this; - } - - const GroupBuilder& TreeRegexp::RootBuilder() const - { - return rootGroupBuilder; - } - - std::optional TreeRegexp::MatchToGroup(const std::string& text) const - { - const auto matchResult = regexStrategy->Match(text); - if (!matchResult) - return std::nullopt; - - std::size_t index = 0; - return rootGroupBuilder.Build(*matchResult, index); - } -} diff --git a/cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp b/cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp deleted file mode 100644 index 3b3b9ffa..00000000 --- a/cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CUCUMBER_EXPRESSION_TREE_REGEXP_HPP -#define CUCUMBER_EXPRESSION_TREE_REGEXP_HPP - -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegexStrategy.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct GroupBuilder - { - void Add(GroupBuilder groupBuilder); - - void SetNonCapturing(); - [[nodiscard]] bool IsCapturing() const; - - void SetPattern(std::string_view pattern); - - void MoveChildrenTo(GroupBuilder& target); - - [[nodiscard]] const std::list& Children() const; - [[nodiscard]] std::string_view Pattern() const; - - [[nodiscard]] ArgumentGroup Build(std::span> match, std::size_t& index) const; - - private: - std::string_view pattern; - bool capturing{ true }; - std::list children; - }; - - struct TreeRegexp - { - explicit TreeRegexp(std::string_view pattern); - TreeRegexp(const TreeRegexp& other); - TreeRegexp& operator=(const TreeRegexp& other); - TreeRegexp(TreeRegexp&&) = default; - TreeRegexp& operator=(TreeRegexp&&) = default; - ~TreeRegexp() = default; - - [[nodiscard]] const GroupBuilder& RootBuilder() const; - - [[nodiscard]] std::optional MatchToGroup(const std::string& text) const; - - private: - std::string storedPattern; - GroupBuilder rootGroupBuilder; - std::unique_ptr regexStrategy; - }; -} - -#endif diff --git a/cucumber_cpp/library/cucumber_expression/test/CMakeLists.txt b/cucumber_cpp/library/cucumber_expression/test/CMakeLists.txt deleted file mode 100644 index 651932c8..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -add_executable(cucumber_cpp.library.cucumber_expression.test) -add_test(NAME cucumber_cpp.library.cucumber_expression.test COMMAND cucumber_cpp.library.cucumber_expression.test) - -target_link_libraries(cucumber_cpp.library.cucumber_expression.test PUBLIC - cucumber_cpp.library.cucumber_expression - GTest::gmock - GTest::gmock_main - yaml-cpp::yaml-cpp -) - -target_sources(cucumber_cpp.library.cucumber_expression.test PRIVATE - TestExpression.cpp - TestExpressionParser.cpp - TestExpressionTokenizer.cpp - TestRegexStrategy.cpp - TestRegexStrategyFactory.cpp - TestRegularExpression.cpp - TestTransformation.cpp - TestTreeRegexp.cpp -) - -if (CCR_HAS_RE2) - target_compile_definitions(cucumber_cpp.library.cucumber_expression.test PRIVATE - CCR_HAS_RE2 - ) -endif() - -add_custom_command( - TARGET cucumber_cpp.library.cucumber_expression.test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/testdata - $/testdata -) - -add_custom_command( - TARGET cucumber_cpp.library.cucumber_expression.test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/testdata - $/../testdata -) diff --git a/cucumber_cpp/library/cucumber_expression/test/TestExpression.cpp b/cucumber_cpp/library/cucumber_expression/test/TestExpression.cpp deleted file mode 100644 index 8dbb2f6a..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestExpression.cpp +++ /dev/null @@ -1,288 +0,0 @@ - -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "cucumber_cpp/library/cucumber_expression/Expression.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "fmt/format.h" -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/parse.h" -#include "yaml-cpp/yaml.h" -#include "gmock/gmock.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - std::vector> GetTestData(const std::filesystem::path& path) - { - std::vector> testdata; - - for (const auto& file : std::filesystem::directory_iterator(path)) - if (file.is_regular_file() && file.path().extension() == ".yaml") - testdata.emplace_back(file.path().string(), YAML::LoadFile(file.path().string())); - - return testdata; - } - - std::string FormatTestFailureMessage(const std::string& file, const YAML::Node& node, const Expression& expression) - { - return fmt::format("file: {}\n" - "failed to match {}\n" - "regex {}\n" - "against {}", - file, node["expression"].as(), expression.Pattern(), node["text"].as()); - } - } - - struct TestExpression : testing::Test - { - ParameterRegistry parameterRegistry{ {} }; - - template - std::optional Match(const std::string& expr, const std::string& text) - { - Expression expression{ expr, parameterRegistry }; - auto args = expression.MatchToArguments(text); - - if (!args.has_value()) - return std::nullopt; - else - return args.value()[0].GetValue(); - } - }; - - TEST_F(TestExpression, TestFromFiles) - { - std::filesystem::path testdataPath = "testdata/cucumber-expression/matching"; - - for (const auto& [file, testdata] : GetTestData(testdataPath)) - { - if (testdata["exception"]) - ASSERT_ANY_THROW(Match(testdata["expression"].as(), testdata["text"].as())) - << fmt::format("Test failed for file: {}", file); - else - { - if (testdata["expected_args"].IsNull()) - ASSERT_THAT(Match(testdata["expression"].as(), testdata["text"].as()), testing::IsFalse()); - else - { - const auto expression = Expression{ testdata["expression"].as(), parameterRegistry }; - const auto matchOpt = expression.MatchToArguments(testdata["text"].as()); - - ASSERT_THAT(matchOpt, testing::IsTrue()) << FormatTestFailureMessage(file, testdata, expression); - - const auto& match = *matchOpt; - for (std::size_t i = 0; i < testdata["expected_args"].size(); ++i) - { - const auto& argument = match[i]; - - if (argument.Name() == "") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "int") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "float") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "word") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "string") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "bigdecimal") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "biginteger") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "byte") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "short") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "long") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else if (argument.Name() == "double") - EXPECT_THAT(argument.GetValue(), testdata["expected_args"][i].as()) << FormatTestFailureMessage(file, testdata, expression); - - else - FAIL() << "Unknown type: " << argument.Name() << " for:\n" - << FormatTestFailureMessage(file, testdata, expression); - } - } - } - } - } - - TEST_F(TestExpression, MatchFloat) - { - EXPECT_THAT(Match(R"__({float})__", R"__()__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(.)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(,)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(-)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(E)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1,)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(,1)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1.)__"), testing::IsFalse()); - - EXPECT_THAT(Match(R"__({float})__", R"__(1)__").value(), testing::FloatNear(1.0f, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-1)__").value(), testing::FloatNear(-1.0f, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(1.1)__").value(), testing::FloatNear(1.1f, std::numeric_limits::epsilon())); - - EXPECT_THAT(Match(R"__({float})__", R"__(1,000)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1,000,0)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1,000.1)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1,000,10)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1,0.1)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(1,000,000.1)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(-1.1)__").value(), testing::FloatNear(-1.1f, std::numeric_limits::epsilon())); - - EXPECT_THAT(Match(R"__({float})__", R"__(.1)__").value(), testing::FloatNear(0.1f, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1)__").value(), testing::FloatNear(-0.1f, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1000001)__").value(), testing::FloatNear(-0.1000001f, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(1E1)__").value(), testing::FloatNear(10.0, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(.1E1)__").value(), testing::FloatNear(1, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(1,E1)__"), testing::IsFalse()); - EXPECT_THAT(Match(R"__({float})__", R"__(-.01)__").value(), testing::FloatNear(-0.01, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1E-1)__").value(), testing::FloatNear(-0.01, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1E-2)__").value(), testing::FloatNear(-0.001, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1E+1)__").value(), testing::FloatNear(-1, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1E+2)__").value(), testing::FloatNear(-10, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1E1)__").value(), testing::FloatNear(-1, std::numeric_limits::epsilon())); - EXPECT_THAT(Match(R"__({float})__", R"__(-.1E2)__").value(), testing::FloatNear(-10, std::numeric_limits::epsilon())); - } - - TEST_F(TestExpression, FloatWithZero) - { - EXPECT_THAT(Match(R"__({float})__", R"__(0)__").value(), testing::FloatNear(0.0f, std::numeric_limits::epsilon())); - } - - TEST_F(TestExpression, MatchAnonymous) - { - EXPECT_THAT(Match(R"__({})__", R"__(0.22)__").value(), testing::StrEq("0.22")); - } - - TEST_F(TestExpression, MatchCustom) - { - struct CustomType - { - std::optional text; - std::optional number; - }; - - parameterRegistry.AddParameter>("textAndOrNumber", { R"(([A-Z]+)?(?: )?([0-9]+)?)" }, - [](const ConvertFunctionArg& matches) -> std::optional - { - std::optional text{ matches[0] }; - std::optional number{ matches[1].has_value() ? StringTo(matches[1].value()) : std::optional{ std::nullopt } }; - return CustomType{ .text = text, .number = number }; - }); - - auto matchString{ Match(R"__({textAndOrNumber})__", R"__(ABC)__") }; - EXPECT_THAT(matchString, testing::IsTrue()); - EXPECT_THAT(matchString.value().text.value(), testing::StrEq("ABC")); - EXPECT_THAT(matchString.value().number, testing::IsFalse()); - - auto matchInt{ Match(R"__({textAndOrNumber})__", R"__(123)__") }; - EXPECT_THAT(matchInt, testing::IsTrue()); - EXPECT_THAT(matchInt.value().text, testing::IsFalse()); - EXPECT_THAT(matchInt.value().number.value(), testing::Eq(123)); - - auto matchStringAndInt{ Match(R"__({textAndOrNumber})__", R"__(ABC 123)__") }; - EXPECT_THAT(matchStringAndInt, testing::IsTrue()); - EXPECT_THAT(matchStringAndInt.value().text.value(), testing::StrEq("ABC")); - EXPECT_THAT(matchStringAndInt.value().number.value(), testing::Eq(123)); - } - - TEST_F(TestExpression, ExposeSource) - { - auto expr = "I have {int} cuke(s)"; - Expression expression{ expr, parameterRegistry }; - EXPECT_THAT(expr, testing::StrEq(expression.Source())); - } - - TEST_F(TestExpression, MatchBoolean) - { - EXPECT_THAT(Match(R"__({bool})__", R"__(true)__").value(), testing::IsTrue()); - EXPECT_THAT(Match(R"__({bool})__", R"__(1)__").value(), testing::IsTrue()); - EXPECT_THAT(Match(R"__({bool})__", R"__(yes)__").value(), testing::IsTrue()); - EXPECT_THAT(Match(R"__({bool})__", R"__(on)__").value(), testing::IsTrue()); - EXPECT_THAT(Match(R"__({bool})__", R"__(enabled)__").value(), testing::IsTrue()); - EXPECT_THAT(Match(R"__({bool})__", R"__(active)__").value(), testing::IsTrue()); - - EXPECT_THAT(Match(R"__({bool})__", R"__(false)__").value(), testing::IsFalse()); - EXPECT_THAT(Match(R"__({bool})__", R"__(0)__").value(), testing::IsFalse()); - EXPECT_THAT(Match(R"__({bool})__", R"__(2)__").value(), testing::IsFalse()); - EXPECT_THAT(Match(R"__({bool})__", R"__(off)__").value(), testing::IsFalse()); - EXPECT_THAT(Match(R"__({bool})__", R"__(foo)__").value(), testing::IsFalse()); - } - - TEST_F(TestExpression, ThrowUnknownParameterType) - { - using namespace std::string_literals; - const auto expr = "I have {doesnotexist} cuke(s)"s; - - try - { - Expression expression{ expr, parameterRegistry }; - FAIL() << "Expected UndefinedParameterTypeError to be thrown"; - } - catch (const UndefinedParameterTypeError& e) - { - EXPECT_THAT(e.what(), testing::StrEq("This Cucumber Expression has a problem at column 8:\n" - "\n" - "I have {doesnotexist} cuke(s)\n" - " ^------------^\n" - "Undefined parameter type 'doesnotexist'\n" - "Please register a ParameterType for 'doesnotexist'\n")); - } - } - - TEST_F(TestExpression, ThrowDuplicateAnonymousParameterError) - { - try - { - parameterRegistry.AddParameter("", { ".*" }, - [](const ConvertFunctionArg& matches) -> std::string - { - return matches[0].value(); - }); - FAIL() << "Expected CucumberExpressionError to be thrown"; - } - catch (const CucumberExpressionError& e) - { - EXPECT_THAT(e.what(), testing::StrEq("The anonymous parameter type has already been defined")); - } - } - - TEST_F(TestExpression, ThrowDuplicateParameterError) - { - try - { - parameterRegistry.AddParameter("word", { ".*" }, - [](const ConvertFunctionArg& matches) -> std::string - { - return matches[0].value(); - }); - FAIL() << "Expected CucumberExpressionError to be thrown"; - } - catch (const CucumberExpressionError& e) - { - EXPECT_THAT(e.what(), testing::StrEq("There is already a parameter with name word")); - } - } -} diff --git a/cucumber_cpp/library/cucumber_expression/test/TestExpressionParser.cpp b/cucumber_cpp/library/cucumber_expression/test/TestExpressionParser.cpp deleted file mode 100644 index 3b3890c4..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestExpressionParser.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/ExpressionParser.hpp" -#include "fmt/format.h" -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/parse.h" -#include "yaml-cpp/yaml.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - const std::map tokenTypeMap = { - { "TEXT_NODE", NodeType::text }, - { "OPTIONAL_NODE", NodeType::optional }, - { "ALTERNATION_NODE", NodeType::alternation }, - { "ALTERNATIVE_NODE", NodeType::alternative }, - { "PARAMETER_NODE", NodeType::parameter }, - { "EXPRESSION_NODE", NodeType::expression }, - }; - - Node CreateNode(const YAML::Node& yaml) - { - if (yaml["nodes"]) - { - Node node{ - tokenTypeMap.at(yaml["type"].as()), - yaml["start"].as(), - yaml["end"].as(), - std::vector{}, - }; - - for (const auto& child : yaml["nodes"]) - node.Children().push_back(CreateNode(child)); - - return node; - } - else - return { - tokenTypeMap.at(yaml["type"].as()), - yaml["start"].as(), - yaml["end"].as(), - yaml["token"].as(), - }; - } - - Node CreateNodes(const YAML::Node& yaml) - { - return CreateNode(yaml); - } - - std::vector> GetTestData(const std::filesystem::path& path) - { - std::vector> testdata; - - for (const auto& file : std::filesystem::directory_iterator(path)) - if (file.is_regular_file() && file.path().extension() == ".yaml") - testdata.emplace_back(file.path().string(), YAML::LoadFile(file.path().string())); - - return testdata; - } - - void PrintTo(const Node& node, std::ostream* os) - { - std::visit([&node, &os](const auto& arg) - { - if constexpr (std::is_same_v, std::string>) - { - *os << "{type:" << static_cast(node.Type()) << " start:" << node.Start() << " end:" << node.End() << " text: " << arg << "}"; - } - else if constexpr (std::is_same_v, std::vector>) - { - *os << "{type:" << static_cast(node.Type()) << " start:" << node.Start() << " end:" << node.End() << " children: ["; - for (const auto& child : arg) - { - PrintTo(child, os); - *os << ", "; - } - *os << "]}"; - } - else - { - *os << "{type:" << static_cast(node.Type()) << " start:" << node.Start() << " end:" << node.End() << "}"; - } - }, - node.GetLeafNodes()); - } - } - - TEST(TestExpressionParser, TestFromFiles) - { - std::filesystem::path testdataPath = "testdata/cucumber-expression/parser"; - - for (const auto& [file, testdata] : GetTestData(testdataPath)) - { - if (testdata["exception"]) - ASSERT_ANY_THROW(ExpressionParser{}.Parse(testdata["expression"].as())) - << fmt::format("Test failed for file: {}", file); - else - { - const auto actual = ExpressionParser{}.Parse(testdata["expression"].as()); - const auto expected = CreateNode(testdata["expected_ast"]); - ASSERT_THAT(actual, testing::Eq(expected)) - << fmt::format("Test failed for file: {}", file); - } - } - } -} diff --git a/cucumber_cpp/library/cucumber_expression/test/TestExpressionTokenizer.cpp b/cucumber_cpp/library/cucumber_expression/test/TestExpressionTokenizer.cpp deleted file mode 100644 index b9c12cd2..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestExpressionTokenizer.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Ast.hpp" -#include "cucumber_cpp/library/cucumber_expression/ExpressionTokenizer.hpp" -#include "fmt/format.h" -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/parse.h" -#include "yaml-cpp/yaml.h" -#include "gmock/gmock.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - const std::map tokenTypeMap = { - { "START_OF_LINE", TokenType::startOfLine }, - { "END_OF_LINE", TokenType::endOfLine }, - { "TEXT", TokenType::text }, - { "WHITE_SPACE", TokenType::whiteSpace }, - { "BEGIN_PARAMETER", TokenType::beginParameter }, - { "END_PARAMETER", TokenType::endParameter }, - { "BEGIN_OPTIONAL", TokenType::beginOptional }, - { "END_OPTIONAL", TokenType::endOptional }, - { "ALTERNATION", TokenType::alternation } - }; - - std::vector CreateTokens(const YAML::Node& node) - { - std::vector tokens; - tokens.reserve(node.size()); - for (const auto& expectedToken : node) - tokens.emplace_back( - tokenTypeMap.at(expectedToken["type"].as()), - expectedToken["text"].as(), - expectedToken["start"].as(), - expectedToken["end"].as()); - return tokens; - } - - std::vector> GetTestData(const std::filesystem::path& path) - { - std::vector> testdata; - - for (const auto& file : std::filesystem::directory_iterator(path)) - if (file.is_regular_file() && file.path().extension() == ".yaml") - testdata.emplace_back(file.path().string(), YAML::LoadFile(file.path().string())); - - return testdata; - } - } - - TEST(TestExpressionTokenizer, TestFromFiles) - { - std::filesystem::path testdataPath = "testdata/cucumber-expression/tokenizer"; - - for (const auto& [file, testdata] : GetTestData(testdataPath)) - if (testdata["exception"]) - ASSERT_ANY_THROW(ExpressionTokenizer{}.Tokenize(testdata["expression"].as())) - << fmt::format("Test failed for file: {}", file); - else - { - const auto actual = ExpressionTokenizer{}.Tokenize(testdata["expression"].as()); - const auto expected = CreateTokens(testdata["expected_tokens"]); - ASSERT_THAT(actual, testing::ElementsAreArray(expected)) - << fmt::format("Test failed for file: {}", file); - } - } - - TEST(TestExpressionTokenizer, TestNameOf) - { - EXPECT_THAT(Token::NameOf(TokenType::startOfLine), testing::StrEq("startOfLine")); - EXPECT_THAT(Token::NameOf(TokenType::endOfLine), testing::StrEq("endOfLine")); - EXPECT_THAT(Token::NameOf(TokenType::whiteSpace), testing::StrEq("whiteSpace")); - EXPECT_THAT(Token::NameOf(TokenType::beginOptional), testing::StrEq("beginOptional")); - EXPECT_THAT(Token::NameOf(TokenType::endOptional), testing::StrEq("endOptional")); - EXPECT_THAT(Token::NameOf(TokenType::beginParameter), testing::StrEq("beginParameter")); - EXPECT_THAT(Token::NameOf(TokenType::endParameter), testing::StrEq("endParameter")); - EXPECT_THAT(Token::NameOf(TokenType::alternation), testing::StrEq("alternation")); - EXPECT_THAT(Token::NameOf(TokenType::text), testing::StrEq("text")); - } -} diff --git a/cucumber_cpp/library/cucumber_expression/test/TestRegexStrategy.cpp b/cucumber_cpp/library/cucumber_expression/test/TestRegexStrategy.cpp deleted file mode 100644 index 63c8eecd..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestRegexStrategy.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp" -#ifdef CCR_HAS_RE2 -#include "cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp" -#endif -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - template - struct TestRegexStrategy : testing::Test - {}; - -#ifdef CCR_HAS_RE2 - using RegexStrategyTypes = testing::Types; -#else - using RegexStrategyTypes = testing::Types; -#endif - TYPED_TEST_SUITE(TestRegexStrategy, RegexStrategyTypes); - - TYPED_TEST(TestRegexStrategy, ReturnsNulloptWhenNoMatch) - { - TypeParam strategy{ R"__(hello)__" }; - - EXPECT_THAT(strategy.Match("world"), testing::IsFalse()); - } - - TYPED_TEST(TestRegexStrategy, ReturnsVectorWhenPatternMatches) - { - TypeParam strategy{ R"__((\d+))__" }; - - const auto result = strategy.Match("abc 42 def"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(0)->value, testing::StrEq("42")); // whole match - EXPECT_THAT(result->at(1)->value, testing::StrEq("42")); // first capture group - } - - TYPED_TEST(TestRegexStrategy, WholeMatchIsAtIndexZero) - { - TypeParam strategy{ R"__((\w+)\s+(\w+))__" }; - - const auto result = strategy.Match("hello world"); - - ASSERT_THAT(result, testing::IsTrue()); - ASSERT_THAT(result->size(), testing::Eq(3)); - EXPECT_THAT(result->at(0)->value, testing::StrEq("hello world")); // whole match - EXPECT_THAT(result->at(1)->value, testing::StrEq("hello")); // first capture group - EXPECT_THAT(result->at(2)->value, testing::StrEq("world")); // second capture group - } - - TYPED_TEST(TestRegexStrategy, MatchesNegativeInteger) - { - TypeParam strategy{ R"__((-?\d+))__" }; - - const auto result = strategy.Match("-22"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(1)->value, testing::StrEq("-22")); - } - - TYPED_TEST(TestRegexStrategy, MatchesEmptyCapture) - { - TypeParam strategy{ R"__(^The value equals "([^"]*)"$)__" }; - - const auto result = strategy.Match(R"__(The value equals "")__"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(1)->value, testing::StrEq("")); - } - - TYPED_TEST(TestRegexStrategy, ReturnsStartPositionOfWholeMatch) - { - TypeParam strategy{ R"__((\d+))__" }; - - const auto result = strategy.Match("abc 42 def"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(0)->start, testing::Eq(4)); // "42" starts at index 4 - EXPECT_THAT(result->at(0)->end, testing::Eq(6)); // "42" ends after index 6 - } - - TYPED_TEST(TestRegexStrategy, ReturnsStartPositionOfCaptureGroup) - { - TypeParam strategy{ R"__((\w+)\s+(\w+))__" }; - - const auto result = strategy.Match("hello world"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(1)->start, testing::Eq(0)); // "hello" starts at 0 - EXPECT_THAT(result->at(1)->end, testing::Eq(5)); // "hello" ends at 5 - EXPECT_THAT(result->at(2)->start, testing::Eq(6)); // "world" starts at 6 - EXPECT_THAT(result->at(2)->end, testing::Eq(11)); // "world" ends at 11 - } - - TYPED_TEST(TestRegexStrategy, UnmatchedGroupIsNullopt) - { - TypeParam strategy{ R"__(^Something( with an optional argument)?)__" }; - - const auto result = strategy.Match("Something"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(1), testing::IsFalse()); - } - -#ifdef CCR_HAS_RE2 - TEST(Re2RegexStrategy, ThrowsOnInvalidPattern) - { - EXPECT_THROW(Re2RegexStrategy{ R"__([invalid)__" }, std::invalid_argument); - } -#endif -} - diff --git a/cucumber_cpp/library/cucumber_expression/test/TestRegexStrategyFactory.cpp b/cucumber_cpp/library/cucumber_expression/test/TestRegexStrategyFactory.cpp deleted file mode 100644 index 6e991c46..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestRegexStrategyFactory.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/RegexStrategyFactory.hpp" -#include "cucumber_cpp/library/cucumber_expression/StdRegexStrategy.hpp" -#ifdef CCR_HAS_RE2 -#include "cucumber_cpp/library/cucumber_expression/Re2RegexStrategy.hpp" -#endif -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - TEST(RegexStrategyFactory, ReturnsNonNullStrategy) - { - const auto strategy = CreateRegexStrategy(R"__((\d+))__"); - - EXPECT_THAT(strategy, testing::NotNull()); - } - - TEST(RegexStrategyFactory, ReturnedStrategyCanMatch) - { - const auto strategy = CreateRegexStrategy(R"__((\d+))__"); - - const auto result = strategy->Match("abc 42 def"); - - ASSERT_THAT(result, testing::IsTrue()); - EXPECT_THAT(result->at(1)->value, testing::StrEq("42")); - } - -#ifdef CCR_HAS_RE2 - TEST(RegexStrategyFactory, ReturnsRe2StrategyForSupportedPattern) - { - const auto strategy = CreateRegexStrategy(R"__((\d+))__"); - - EXPECT_THAT(dynamic_cast(strategy.get()), testing::NotNull()); - } - - TEST(RegexStrategyFactory, FallsBackToStdStrategyForUnsupportedPattern) - { - // lookahead (?=) is not supported by RE2 - const auto strategy = CreateRegexStrategy(R"__((?=.*\d)\d+)__"); - - EXPECT_THAT(dynamic_cast(strategy.get()), testing::NotNull()); - } -#else - TEST(RegexStrategyFactory, ReturnsStdStrategy) - { - const auto strategy = CreateRegexStrategy(R"__((\d+))__"); - - EXPECT_THAT(dynamic_cast(strategy.get()), testing::NotNull()); - } -#endif -} diff --git a/cucumber_cpp/library/cucumber_expression/test/TestRegularExpression.cpp b/cucumber_cpp/library/cucumber_expression/test/TestRegularExpression.cpp deleted file mode 100644 index a1dceb2b..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestRegularExpression.cpp +++ /dev/null @@ -1,120 +0,0 @@ - -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegularExpression.hpp" -#include "gmock/gmock.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - struct TestRegularExpression : testing::Test - { - ParameterRegistry parameterRegistry{ {} }; - - template - void MatchOnFirst(std::string expr, const std::string& text, const T& expected) - { - RegularExpression expression{ std::move(expr), parameterRegistry }; - - const auto matches = expression.MatchToArguments(text); - - ASSERT_THAT(matches, testing::IsTrue()); - ASSERT_THAT(matches->size(), testing::Eq(1)); - EXPECT_THAT(matches->at(0).GetValue(), testing::Eq(expected)); - } - }; - - TEST_F(TestRegularExpression, DoesNotTransformByDefault) - { - MatchOnFirst(R"__((\d\d))__", "22", "22"); - } - - TEST_F(TestRegularExpression, DoesNotTransformAnonymous) - { - MatchOnFirst(R"__((.*))__", "22", "22"); - } - - TEST_F(TestRegularExpression, TransformsNegativeInt) - { - MatchOnFirst(R"__((-?\d+))__", "-22", -22); - } - - TEST_F(TestRegularExpression, TransformsPositiveInt) - { - MatchOnFirst(R"__((\d+))__", "22", 22); - } - - TEST_F(TestRegularExpression, ReturnsNulloptWhenDoesNotMatch) - { - RegularExpression expression{ R"__(hello)__", parameterRegistry }; - - const auto matches = expression.MatchToArguments("world"); - - ASSERT_THAT(matches, testing::IsFalse()); - } - - TEST_F(TestRegularExpression, MatchesEmptyString) - { - MatchOnFirst(R"__(^The value equals "([^"]*)"$)__", R"__(The value equals "")__", ""); - } - - TEST_F(TestRegularExpression, MatchesNestedCaptureGroupWithoutMatch) - { - RegularExpression expression{ R"__(^a user( named "([^"]*)")?$)__", parameterRegistry }; - - const auto matches = expression.MatchToArguments("a user"); - - ASSERT_THAT(matches, testing::IsTrue()); - ASSERT_THAT(matches->size(), testing::Eq(1)); - EXPECT_THAT(matches->at(0).GetValue>(), testing::IsFalse()); - } - - TEST_F(TestRegularExpression, MatchesNestedCaptureGroupWithMatch) - { - MatchOnFirst(R"__(^a user( named "([^"]*)")?$)__", R"__(a user named "Charlie")__", "Charlie"); - } - - TEST_F(TestRegularExpression, MatchesCaptureGroupNestedInOptionalOne) - { - RegularExpression expression{ R"__(^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$)__", parameterRegistry }; - - const auto matches1 = expression.MatchToArguments("a purchase"); - ASSERT_THAT(matches1, testing::IsTrue()); - ASSERT_THAT(matches1->size(), testing::Eq(2)); - EXPECT_THAT(matches1->at(0).GetValue>(), testing::IsFalse()); - EXPECT_THAT(matches1->at(1).GetValue>(), testing::IsFalse()); - - const auto matches2 = expression.MatchToArguments("a purchase for $33"); - ASSERT_THAT(matches2, testing::IsTrue()); - ASSERT_THAT(matches2->size(), testing::Eq(2)); - EXPECT_THAT(matches2->at(0).GetValue>(), testing::IsFalse()); - EXPECT_THAT(matches2->at(1).GetValue>(), testing::Eq(33)); - - const auto matches3 = expression.MatchToArguments("a pre buyer fee model purchase"); - ASSERT_THAT(matches3, testing::IsTrue()); - ASSERT_THAT(matches3->size(), testing::Eq(2)); - EXPECT_THAT(matches3->at(0).GetValue>(), testing::Eq("pre buyer fee model ")); - EXPECT_THAT(matches3->at(1).GetValue>(), testing::IsFalse()); - } - - TEST_F(TestRegularExpression, IgnoresNonCapturingGroups) - { - RegularExpression expression{ R"__((\S+) ?(can|cannot)? (?:delete|cancel) the (\d+)(?:st|nd|rd|th) (attachment|slide) ?(?:upload)?)__", parameterRegistry }; - - const auto matches = expression.MatchToArguments("I can cancel the 1st slide upload"); - ASSERT_THAT(matches, testing::IsTrue()); - ASSERT_THAT(matches->size(), testing::Eq(4)); - EXPECT_THAT(matches->at(0).GetValue(), testing::Eq("I")); - EXPECT_THAT(matches->at(1).GetValue(), testing::Eq("can")); - EXPECT_THAT(matches->at(2).GetValue(), testing::Eq(1)); - EXPECT_THAT(matches->at(3).GetValue(), testing::Eq("slide")); - } -} diff --git a/cucumber_cpp/library/cucumber_expression/test/TestTransformation.cpp b/cucumber_cpp/library/cucumber_expression/test/TestTransformation.cpp deleted file mode 100644 index 82210908..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestTransformation.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "cucumber_cpp/library/cucumber_expression/Expression.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "fmt/format.h" -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/parse.h" -#include "yaml-cpp/yaml.h" -#include "gmock/gmock.h" -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - namespace - { - std::vector> GetTestData(const std::filesystem::path& path) - { - std::vector> testdata; - - for (const auto& file : std::filesystem::directory_iterator(path)) - if (file.is_regular_file() && file.path().extension() == ".yaml") - testdata.emplace_back(file.path().string(), YAML::LoadFile(file.path().string())); - - return testdata; - } - - std::string FormatMessage(const YAML::Node& node, const Expression& expression) - { - return fmt::format("failed to match {}\n" - "regex {}\n" - "against {}", - node["expression"].as(), expression.Pattern(), node["text"].as()); - } - } - - TEST(TestTransformation, TestFromFiles) - { - std::filesystem::path testdataPath = "testdata/cucumber-expression/transformation"; - ParameterRegistry parameterRegistry{ {} }; - - for (const auto& [file, testdata] : GetTestData(testdataPath)) - { - const auto expression = Expression{ testdata["expression"].as(), parameterRegistry }; - const auto actualRegex = expression.Pattern(); - - EXPECT_THAT(actualRegex, testing::StrEq(testdata["expected_regex"].as())) - << fmt::format("Test failed for {}", testdata["expression"].as()); - } - } -} diff --git a/cucumber_cpp/library/cucumber_expression/test/TestTreeRegexp.cpp b/cucumber_cpp/library/cucumber_expression/test/TestTreeRegexp.cpp deleted file mode 100644 index bec78ae8..00000000 --- a/cucumber_cpp/library/cucumber_expression/test/TestTreeRegexp.cpp +++ /dev/null @@ -1,185 +0,0 @@ - -#include "cucumber_cpp/library/cucumber_expression/TreeRegexp.hpp" -#include "gmock/gmock.h" -#include -#include -#include - -namespace cucumber_cpp::library::cucumber_expression -{ - using namespace std::string_view_literals; - - namespace - { - std::string_view BuilderPattern(const GroupBuilder& builder) - { - return builder.Pattern(); - } - } - - TEST(TestTreeRegexp, exposes_group_source) - { - TreeRegexp treeRegexp{ R"__((a(?:b)?)(c))__" }; - EXPECT_THAT(treeRegexp.RootBuilder().Children().begin()->Pattern(), testing::StrEq("a(?:b)?"sv)); - EXPECT_THAT(std::next(treeRegexp.RootBuilder().Children().begin())->Pattern(), testing::StrEq("c"sv)); - } - - TEST(TestTreeRegexp, builds_tree) - { - TreeRegexp treeRegexp{ R"__((a(?:b)?)(c))__" }; - const auto group = *treeRegexp.MatchToGroup("ac"); - EXPECT_THAT(group.value.value(), testing::StrEq("ac")); - EXPECT_THAT(group.children[0].value.value(), testing::StrEq("a")); - EXPECT_THAT(group.children[0].children.empty(), testing::IsTrue()); - EXPECT_THAT(group.children[1].value.value(), testing::StrEq("c")); - } - - TEST(TestTreeRegexp, ignores_non_capture_group_as_a_non_capturing_group) - { - TreeRegexp treeRegexp{ R"__(a(?:b)(c))__" }; - const auto group = *treeRegexp.MatchToGroup("abc"); - EXPECT_THAT(group.value.value(), testing::StrEq("abc")); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - } - - TEST(TestTreeRegexp, ignores_negative_lookahead_as_a_non_capturing_group) - { - TreeRegexp treeRegexp{ R"__(a(?!b)(.+))__" }; - const auto group = *treeRegexp.MatchToGroup("aBc"); - EXPECT_THAT(group.value.value(), testing::StrEq("aBc")); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - } - - TEST(TestTreeRegexp, ignores_positive_lookahead_as_a_non_capturing_group) - { - TreeRegexp treeRegexp{ R"__(a(?=[b])(.+))__" }; - const auto group = *treeRegexp.MatchToGroup("abc"); - EXPECT_THAT(group.value.value(), testing::StrEq("abc")); - EXPECT_THAT(group.children.size(), 1); - EXPECT_THAT(group.children[0].value.value(), testing::StrEq("bc")); - } - - TEST(TestTreeRegexp, DISABLED_ignores_positive_lookbehind_as_a_non_capturing_group) - { - // std::regex does not support positive lookbehind - TreeRegexp treeRegexp{ R"__(a(.+)(?<=c)$)__" }; - const auto group = *treeRegexp.MatchToGroup("abc"); - EXPECT_THAT(group.value.value(), testing::StrEq("abc")); - EXPECT_THAT(group.children.size(), 1); - EXPECT_THAT(group.children[0].value.value(), testing::StrEq("bc")); - } - - TEST(TestTreeRegexp, DISABLED_ignores_negative_lookbehind_as_a_non_capturing_group) - { - // std::regex does not support negative lookbehind - TreeRegexp treeRegexp{ R"__(a(.+?)(?b)c)__" }; - const auto group = *treeRegexp.MatchToGroup("abc"); - EXPECT_THAT(group.value.value(), testing::StrEq("abc")); - EXPECT_THAT(group.children.size(), 1); - EXPECT_THAT(group.children[0].value.value(), testing::StrEq("b")); - } - - TEST(TestTreeRegexp, matches_optional_group) - { - TreeRegexp treeRegexp{ R"__(^Something( with an optional argument)?)__" }; - const auto group = *treeRegexp.MatchToGroup("Something"); - EXPECT_THAT(group.children[0].value, testing::IsFalse()); - } - - TEST(TestTreeRegexp, matches_nested_groups) - { - TreeRegexp treeRegexp{ R"__(^A (\d+) thick line from ((\d+),\s*(\d+),\s*(\d+)) to ((\d+),\s*(\d+),\s*(\d+)))__" }; - const auto group = *treeRegexp.MatchToGroup("A 5 thick line from 10,20,30 to 40,50,60"); - EXPECT_THAT(group.children[0].value.value(), testing::StrEq("5")); - EXPECT_THAT(group.children[1].value.value(), testing::StrEq("10,20,30")); - EXPECT_THAT(group.children[1].children[0].value.value(), testing::StrEq("10")); - EXPECT_THAT(group.children[1].children[1].value.value(), testing::StrEq("20")); - EXPECT_THAT(group.children[1].children[2].value.value(), testing::StrEq("30")); - EXPECT_THAT(group.children[2].value.value(), testing::StrEq("40,50,60")); - EXPECT_THAT(group.children[2].children[0].value.value(), testing::StrEq("40")); - EXPECT_THAT(group.children[2].children[1].value.value(), testing::StrEq("50")); - EXPECT_THAT(group.children[2].children[2].value.value(), testing::StrEq("60")); - } - - TEST(TestTreeRegexp, detects_multiple_non_capturing_groups) - { - TreeRegexp treeRegexp{ R"__((?:a)(:b)(\?c)(d))__" }; - const auto group = *treeRegexp.MatchToGroup("a:b?cd"); - EXPECT_THAT(group.children.size(), testing::Eq(3)); - } - - TEST(TestTreeRegexp, works_with_escaped_backslash) - { - TreeRegexp treeRegexp{ R"__(foo\\(bar|baz))__" }; - const auto group = *treeRegexp.MatchToGroup("foo\\bar"); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - } - - TEST(TestTreeRegexp, works_with_escaped_slash) - { - TreeRegexp treeRegexp{ R"__(^I go to '\/(.+)'$)__" }; - const auto group = *treeRegexp.MatchToGroup("I go to '/hello'"); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - } - - TEST(TestTreeRegexp, works_with_digit_and_word) - { - TreeRegexp treeRegexp{ R"__(^(\d) (\w+)$)__" }; - const auto group = *treeRegexp.MatchToGroup("2 you"); - EXPECT_THAT(group.children.size(), testing::Eq(2)); - } - - TEST(TestTreeRegexp, captures_non_capturing_groups_with_capturing_groups_inside) - { - TreeRegexp treeRegexp{ R"__(the stdout(?: from "(.*?)")?)__" }; - const auto group = *treeRegexp.MatchToGroup("the stdout"); - - EXPECT_THAT(group.value.value(), testing::StrEq("the stdout")); - EXPECT_THAT(group.children[0].value, testing::IsFalse()); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - } - - TEST(TestTreeRegexp, DISABLED_works_with_case_insensitive_flag) - { - // std::regex does not support inline case insensitive flag - TreeRegexp treeRegexp{ R"__(HELLO/i)__" }; - const auto group = *treeRegexp.MatchToGroup("hello"); - EXPECT_THAT(group.value.value(), testing::StrEq("hello")); - } - - TEST(TestTreeRegexp, empty_capturing_group) - { - TreeRegexp treeRegexp{ R"__(())__" }; - const auto group = *treeRegexp.MatchToGroup(""); - EXPECT_THAT(group.value.value(), testing::StrEq("")); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - } - - TEST(TestTreeRegexp, DISABLED_empty_look_ahead) - { - // std::regex does not support positive lookbehind - TreeRegexp treeRegexp{ R"__((?<=))__" }; - const auto group = *treeRegexp.MatchToGroup(""); - EXPECT_THAT(group.value.value(), testing::StrEq("")); - EXPECT_THAT(group.children.size(), testing::Eq(0)); - } - - TEST(TestTreeRegexp, does_not_consider_parenthesis_in_character_class_as_group) - { - TreeRegexp treeRegexp{ R"__(^drawings: ([A-Z, ()]+)$)__" }; - const auto group = *treeRegexp.MatchToGroup("drawings: ONE(TWO)"); - EXPECT_THAT(group.value.value(), testing::StrEq("drawings: ONE(TWO)")); - EXPECT_THAT(group.children.size(), testing::Eq(1)); - EXPECT_THAT(group.children[0].value.value(), testing::StrEq("ONE(TWO)")); - } -} diff --git a/cucumber_cpp/library/engine/CMakeLists.txt b/cucumber_cpp/library/engine/CMakeLists.txt index 83a05986..8344a2f2 100644 --- a/cucumber_cpp/library/engine/CMakeLists.txt +++ b/cucumber_cpp/library/engine/CMakeLists.txt @@ -16,7 +16,7 @@ target_include_directories(cucumber_cpp.library.engine PUBLIC target_link_libraries(cucumber_cpp.library.engine PUBLIC base64 cucumber_cpp.library.util - cucumber_cpp.library.cucumber_expression + cucumber::cucumber_expressions cucumber_cpp.library.support cucumber_cpp.library.assemble cucumber_cpp.library.runtime diff --git a/cucumber_cpp/library/engine/ExecutionContext.cpp b/cucumber_cpp/library/engine/ExecutionContext.cpp index 4b1a6c7c..43d1f117 100644 --- a/cucumber_cpp/library/engine/ExecutionContext.cpp +++ b/cucumber_cpp/library/engine/ExecutionContext.cpp @@ -46,7 +46,7 @@ namespace cucumber_cpp::library::engine return std::nullopt; } - void BroadcastAttachment(const util::Broadcaster& broadCaster, std::string data, cucumber::messages::AttachmentContentEncoding encoding, OptionsOrMediaType mediaType, const util::StepOrHookStarted& stepOrHookStarted) + void BroadcastAttachment(util::Broadcaster& broadCaster, std::string data, cucumber::messages::AttachmentContentEncoding encoding, OptionsOrMediaType mediaType, const util::StepOrHookStarted& stepOrHookStarted) { auto options = std::holds_alternative(mediaType) ? AttachOptions{ .mediaType = std::get(mediaType) } @@ -55,16 +55,20 @@ namespace cucumber_cpp::library::engine auto [testCaseStartedId, testStepId] = ReadTestStepStartedIds(stepOrHookStarted); auto testRunHookStartedId = ReadTestRunHookStartedIds(stepOrHookStarted); - broadCaster.BroadcastEvent(util::MakeShared(cucumber::messages::Attachment{ - .body = std::move(data), - .contentEncoding = encoding, - .fileName = std::move(options.fileName), - .mediaType = std::move(options.mediaType), - .testCaseStartedId = std::move(testCaseStartedId), - .testStepId = std::move(testStepId), - .testRunHookStartedId = std::move(testRunHookStartedId), - .timestamp = util::MakeShared(util::TimestampNow()), - })); + broadCaster.BroadcastEvent([&data, &encoding, &options, &testCaseStartedId, &testStepId, &testRunHookStartedId](cucumber::messages::Envelope& envelope) + { + cucumber::messages::Attachment attachment; + attachment.body = std::move(data); + attachment.contentEncoding = encoding; + attachment.fileName = std::move(options.fileName); + attachment.mediaType = std::move(options.mediaType); + attachment.testCaseStartedId = std::move(testCaseStartedId); + attachment.testStepId = std::move(testStepId); + attachment.testRunHookStartedId = std::move(testRunHookStartedId); + attachment.timestamp = util::TimestampNow(); + + envelope.attachment = std::move(attachment); + }); } } diff --git a/cucumber_cpp/library/engine/test/TestStep.cpp b/cucumber_cpp/library/engine/test/TestStep.cpp index 471a65c5..19b3b803 100644 --- a/cucumber_cpp/library/engine/test/TestStep.cpp +++ b/cucumber_cpp/library/engine/test/TestStep.cpp @@ -1,7 +1,7 @@ +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber/gherkin/IdGenerator.hpp" #include "cucumber/messages/PickleStepArgument.hpp" #include "cucumber_cpp/library/Context.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/engine/ExecutionContext.hpp" #include "cucumber_cpp/library/engine/Step.hpp" #include "cucumber_cpp/library/runtime/NestedTestCaseRunner.hpp" @@ -50,7 +50,7 @@ namespace cucumber_cpp::library::engine util::StepOrHookStarted stepOrHookStarted; cucumber::messages::PickleStepArgument pickleStepArgument; - cucumber_expression::ParameterRegistry parameterRegistry{ cucumber_cpp::library::support::DefinitionRegistration::Instance().GetRegisteredParameters() }; + cucumber::cucumber_expressions::ParameterRegistry parameterRegistry{ cucumber_cpp::library::support::DefinitionRegistration::Instance().GetRegisteredParameters() }; cucumber::gherkin::IdGeneratorPtr idGenerator = std::make_shared(); support::UndefinedParameters undefinedParameters; support::StepRegistry stepRegistry{ parameterRegistry, undefinedParameters, idGenerator }; @@ -75,8 +75,8 @@ namespace cucumber_cpp::library::engine broadcaster, context, stepOrHookStarted, - util::TransformTable(pickleStepArgument.dataTable ? std::make_optional(*(*pickleStepArgument.dataTable)) : std::nullopt), - util::TransformDocString(pickleStepArgument.docString ? std::make_optional(*(*pickleStepArgument.docString)) : std::nullopt), + util::TransformTable(pickleStepArgument.dataTable ? std::make_optional(*pickleStepArgument.dataTable) : std::nullopt), + util::TransformDocString(pickleStepArgument.docString ? std::make_optional(*pickleStepArgument.docString) : std::nullopt), }; }; } diff --git a/cucumber_cpp/library/formatter/CMakeLists.txt b/cucumber_cpp/library/formatter/CMakeLists.txt index 964afa4e..68d3817e 100644 --- a/cucumber_cpp/library/formatter/CMakeLists.txt +++ b/cucumber_cpp/library/formatter/CMakeLists.txt @@ -1,16 +1,14 @@ add_library(cucumber_cpp.library.formatter ${CCR_EXCLUDE_FROM_ALL}) target_sources(cucumber_cpp.library.formatter PRIVATE + ExternalFormatter.cpp + ExternalFormatter.hpp Formatter.cpp Formatter.hpp JunitXmlFormatter.cpp JunitXmlFormatter.hpp MessageFormatter.cpp MessageFormatter.hpp - PrettyFormatter.cpp - PrettyFormatter.hpp - SummaryFormatter.cpp - SummaryFormatter.hpp UsageFormatter.cpp UsageFormatter.hpp ) @@ -24,8 +22,8 @@ target_include_directories(cucumber_cpp.library.formatter PUBLIC ) target_link_libraries(cucumber_cpp.library.formatter PUBLIC - cucumber_cpp.library.cucumber_expression cucumber_cpp.library.formatter.helper + cucumber::pretty_formatter cucumber::query cucumber_cpp.library.support cucumber_cpp.library.util diff --git a/cucumber_cpp/library/formatter/ExternalFormatter.cpp b/cucumber_cpp/library/formatter/ExternalFormatter.cpp new file mode 100644 index 00000000..1b58b822 --- /dev/null +++ b/cucumber_cpp/library/formatter/ExternalFormatter.cpp @@ -0,0 +1,103 @@ +#include "cucumber_cpp/library/formatter/ExternalFormatter.hpp" +#include "cucumber/messages/Envelope.hpp" +#include "cucumber/pretty-formatter/Formatter.hpp" +#include "cucumber/pretty-formatter/PrettyPrinter.hpp" +#include "cucumber/pretty-formatter/ProgressBarPrinter.hpp" +#include "cucumber/pretty-formatter/ProgressPrinter.hpp" +#include "cucumber/pretty-formatter/SummaryPrinter.hpp" +#include "cucumber/pretty-formatter/Theme.hpp" +#include "cucumber_cpp/library/util/Broadcaster.hpp" +#include "nlohmann/json.hpp" +#include +#include +#include +#include +#include +#include + +namespace cucumber_cpp::library::formatter +{ + namespace + { + constexpr std::size_t defaultMaxWidth{ 80 }; + + std::shared_ptr MakeTheme(const nlohmann::json& options) + { + const auto theme = options.value("theme", std::string{ "cucumber" }); + + if (theme == "plain") + return cucumber::pretty_formatter::Theme::Plain(); + if (theme == "none") + return cucumber::pretty_formatter::Theme::None(); + return cucumber::pretty_formatter::Theme::Cucumber(); + } + + // Owns the Tty so its lifetime spans the ProgressBarPrinter. + struct ProgressBarFormatter + : cucumber::pretty_formatter::Formatter + { + ProgressBarFormatter(std::ostream& output, std::shared_ptr theme, std::set options) + : tty{ output } + , printer{ tty, std::move(theme), defaultMaxWidth, [](std::string uri) + { + return uri; + }, + std::move(options) } + {} + + void Update(const cucumber::messages::Envelope& envelope) override + { + printer.Update(envelope); + } + + private: + cucumber::pretty_formatter::ProgressBarPrinter::TtyOstream tty; + cucumber::pretty_formatter::ProgressBarPrinter printer; + }; + } + + ExternalFormatter::ExternalFormatter(util::Broadcaster& broadcaster, std::unique_ptr formatter) + : util::Listener{ broadcaster, [this](const cucumber::messages::Envelope& envelope) + { + this->formatter->Update(envelope); + } } + , formatter{ std::move(formatter) } + {} + + std::unique_ptr MakePrettyPrinter(const nlohmann::json& options, std::ostream& output) + { + using Options = cucumber::pretty_formatter::PrettyPrinter::Options; + + cucumber::pretty_formatter::PrettyPrinter::Factory factory; + factory.Theme(MakeTheme(options)) + .Options(Options::includeAttachments, options.value("include_attachments", true)) + .Options(Options::includeFeatureLine, options.value("include_feature_line", true)) + .Options(Options::includeRuleLine, options.value("include_rule_line", true)) + .Options(Options::useStatusIcon, options.value("use_status_icon", true)); + + return factory.Build(output); + } + + std::unique_ptr MakeSummaryPrinter(const nlohmann::json& options, std::ostream& output) + { + cucumber::pretty_formatter::SummaryPrinter::Factory factory; + factory.Theme(MakeTheme(options)) + .Options(cucumber::pretty_formatter::SummaryPrinter::Options::includeAttachments, options.value("include_attachments", true)); + + return factory.Build(output); + } + + std::unique_ptr MakeProgressPrinter(const nlohmann::json& options, std::ostream& output) + { + return std::make_unique(output, MakeTheme(options), defaultMaxWidth); + } + + std::unique_ptr MakeProgressBarPrinter(const nlohmann::json& options, std::ostream& output) + { + std::set progressBarOptions; + if (options.value("include_attachments", true)) + progressBarOptions.insert(cucumber::pretty_formatter::ProgressBarPrinter::Options::includeAttachments); + + return std::make_unique(output, MakeTheme(options), std::move(progressBarOptions)); + } +} diff --git a/cucumber_cpp/library/formatter/ExternalFormatter.hpp b/cucumber_cpp/library/formatter/ExternalFormatter.hpp new file mode 100644 index 00000000..0fc88a61 --- /dev/null +++ b/cucumber_cpp/library/formatter/ExternalFormatter.hpp @@ -0,0 +1,28 @@ +#ifndef FORMATTER_EXTERNAL_FORMATTER_HPP +#define FORMATTER_EXTERNAL_FORMATTER_HPP + +#include "cucumber/pretty-formatter/Formatter.hpp" +#include "cucumber_cpp/library/util/Broadcaster.hpp" +#include "nlohmann/json_fwd.hpp" +#include +#include + +namespace cucumber_cpp::library::formatter +{ + // Bridges a cucumber::pretty_formatter::Formatter onto the Broadcaster. + struct ExternalFormatter + : util::Listener + { + ExternalFormatter(util::Broadcaster& broadcaster, std::unique_ptr formatter); + + private: + std::unique_ptr formatter; + }; + + std::unique_ptr MakePrettyPrinter(const nlohmann::json& options, std::ostream& output); + std::unique_ptr MakeSummaryPrinter(const nlohmann::json& options, std::ostream& output); + std::unique_ptr MakeProgressPrinter(const nlohmann::json& options, std::ostream& output); + std::unique_ptr MakeProgressBarPrinter(const nlohmann::json& options, std::ostream& output); +} + +#endif diff --git a/cucumber_cpp/library/formatter/JunitXmlFormatter.cpp b/cucumber_cpp/library/formatter/JunitXmlFormatter.cpp index 4ae94d4e..cdc9f0bb 100644 --- a/cucumber_cpp/library/formatter/JunitXmlFormatter.cpp +++ b/cucumber_cpp/library/formatter/JunitXmlFormatter.cpp @@ -65,20 +65,20 @@ namespace cucumber_cpp::library::formatter std::optional timestamp; }; - std::optional MakeFailure(const cucumber::query::Query& query, const std::shared_ptr& testCaseStarted) + std::optional MakeFailure(const cucumber::query::Query& query, const cucumber::messages::TestCaseStarted& testCaseStarted) { - const auto result = query.FindMostSevereTestStepResultBy(testCaseStarted); - if (!result.has_value() || result.value()->status == cucumber::messages::TestStepResultStatus::PASSED) + const auto* result = query.FindMostSevereTestStepResultBy(testCaseStarted); + if (result == nullptr || result->status == cucumber::messages::TestStepResultStatus::PASSED) return std::nullopt; - const auto& testStepResultStatus = result.value(); + const auto* testStepResultStatus = result; return std::optional{ std::in_place, testStepResultStatus->status == cucumber::messages::TestStepResultStatus::SKIPPED ? FailureKind::skipped : FailureKind::failure, - testStepResultStatus->exception ? std::make_optional((*testStepResultStatus->exception)->type) : std::nullopt, - testStepResultStatus->exception ? (*testStepResultStatus->exception)->message : std::nullopt, - testStepResultStatus->exception ? (*testStepResultStatus->exception)->stackTrace : testStepResultStatus->message, + testStepResultStatus->exception ? std::make_optional(testStepResultStatus->exception->type) : std::nullopt, + testStepResultStatus->exception ? testStepResultStatus->exception->message : std::nullopt, + testStepResultStatus->exception ? testStepResultStatus->exception->stackTrace : testStepResultStatus->message, }; } @@ -93,7 +93,7 @@ namespace cucumber_cpp::library::formatter return fmt::format("{:.<76}{}", util::Trim(gherkinStep.keyword) + " " + util::Trim(pickleStep.text), statusString); } - std::string MakeOutput(const cucumber::query::Query& query, const std::shared_ptr& testCaseStarted) + std::string MakeOutput(const cucumber::query::Query& query, const cucumber::messages::TestCaseStarted& testCaseStarted) { const auto testStepFinishedAndTestStep = query.FindTestStepFinishedAndTestStepBy(testCaseStarted); auto outputView = testStepFinishedAndTestStep | @@ -103,9 +103,9 @@ namespace cucumber_cpp::library::formatter }) | std::views::transform([&query](const cucumber::query::TestStepFinishedAndTestStep& pair) { - const auto pickleStep = query.FindPickleStepBy(pair.testStep).value(); - const auto gherkinStep = query.FindStepBy(pickleStep).value(); - return FormatStep(*gherkinStep, *pickleStep, pair.testStepFinished->testStepResult->status); + const auto* pickleStep = query.FindPickleStepBy(*pair.testStep); + const auto* gherkinStep = query.FindStepBy(*pickleStep); + return FormatStep(*gherkinStep, *pickleStep, pair.testStepFinished->testStepResult.status); }); return fmt::format("\n{}\n", fmt::join(outputView, "\n")); @@ -120,14 +120,14 @@ namespace cucumber_cpp::library::formatter const auto allTestCaseStarted = query.FindAllTestCaseStarted(); for (const auto& testCaseStartedPtr : allTestCaseStarted) { - const auto pickle = query.FindPickleBy(testCaseStartedPtr).value(); + const auto* pickle = query.FindPickleBy(testCaseStartedPtr); const auto lineageAndPickle = query.FindLineageBy(testCaseStartedPtr).value(); const auto& lineage = *lineageAndPickle.lineage; const auto durationOpt = query.FindTestCaseDurationBy(testCaseStartedPtr); - testCases.emplace_back(testClassName.value_or(lineage.feature ? lineage.feature->name : pickle->uri), + testCases.emplace_back(testClassName.value_or((lineage.feature != nullptr) ? lineage.feature->name : pickle->uri), namingStrategy->Reduce(lineage, *pickle), - util::DurationToMilliseconds(durationOpt.has_value() ? *durationOpt.value() : cucumber::messages::Duration{}).count(), + util::DurationToMilliseconds(durationOpt.value_or(cucumber::messages::Duration{})).count(), MakeFailure(query, testCaseStartedPtr), MakeOutput(query, testCaseStartedPtr)); } @@ -145,10 +145,10 @@ namespace cucumber_cpp::library::formatter }; const auto testRunDuration = query.FindTestRunDuration(); - const auto testRunStarted = query.FindTestRunStarted(); + const auto* testRunStarted = query.FindTestRunStarted(); return { - .time = util::DurationToMilliseconds(testRunDuration.has_value() ? *testRunDuration.value() : cucumber::messages::Duration{}).count(), + .time = util::DurationToMilliseconds(testRunDuration.value_or(cucumber::messages::Duration{})).count(), .tests = query.CountTestCasesStarted(), .skipped = count(cucumber::messages::TestStepResultStatus::SKIPPED), .failures = count(cucumber::messages::TestStepResultStatus::UNKNOWN) + @@ -158,7 +158,7 @@ namespace cucumber_cpp::library::formatter count(cucumber::messages::TestStepResultStatus::FAILED), .errors = 0, .testCases = MakeTestCases(query, testClassName), - .timestamp = testRunStarted.has_value() ? std::make_optional(util::MakeIso8601Timestamp(*testRunStarted.value()->timestamp)) : std::nullopt, + .timestamp = testRunStarted != nullptr ? std::make_optional(util::MakeIso8601Timestamp(testRunStarted->timestamp)) : std::nullopt, }; } diff --git a/cucumber_cpp/library/formatter/PrettyFormatter.cpp b/cucumber_cpp/library/formatter/PrettyFormatter.cpp deleted file mode 100644 index adccff16..00000000 --- a/cucumber_cpp/library/formatter/PrettyFormatter.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "cucumber_cpp/library/formatter/PrettyFormatter.hpp" -#include "cucumber/messages/Attachment.hpp" -#include "cucumber/messages/Envelope.hpp" -#include "cucumber/messages/TestCaseStarted.hpp" -#include "cucumber/messages/TestRunFinished.hpp" -#include "cucumber/messages/TestStep.hpp" -#include "cucumber/messages/TestStepFinished.hpp" -#include "cucumber/messages/TestStepResultStatus.hpp" -#include "cucumber_cpp/library/formatter/helper/FormatMessages.hpp" -#include "cucumber_cpp/library/formatter/helper/PrintMessages.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "fmt/ostream.h" -#include "nlohmann/json.hpp" -#include "nlohmann/json_fwd.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter -{ - PrettyFormatter::Options::Options(const nlohmann::json& formatOptions) - : includeAttachments{ formatOptions.value("include_attachments", true) } - , includeFeatureLine{ formatOptions.value("include_feature_line", true) } - , includeRuleLine{ formatOptions.value("include_rule_line", true) } - , useStatusIcon{ formatOptions.value("use_status_icon", true) } - , theme{ helper::CreateTheme(formatOptions.value("theme", std::string_view{ "cucumber" })) } - { - } - - void PrettyFormatter::OnEnvelope(const cucumber::messages::Envelope& envelope) - { - if (envelope.testCaseStarted) - { - CalculateIndent(envelope.testCaseStarted.value()); - HandleTestCaseStarted(envelope.testCaseStarted.value()); - } - - if (envelope.attachment) - HandleAttachment(*envelope.attachment.value()); - - if (envelope.testStepFinished) - HandleTestStepFinished(envelope.testStepFinished.value()); - - if (envelope.testRunFinished) - HandleTestRunFinished(*envelope.testRunFinished.value()); - } - - void PrettyFormatter::CalculateIndent(const std::shared_ptr& testCaseStarted) - { - const auto pickle = query.FindPickleBy(testCaseStarted).value(); - const auto lineageAndPickle = query.FindLineageBy(pickle).value(); - const auto& lineage = *lineageAndPickle.lineage; - const auto& scenario = *lineage.scenario; - const auto scenarioLength = helper::Unstyled(helper::FormatPickleTitle(*pickle, scenario, options.theme)).length(); - - const auto testCase = query.FindTestCaseBy(testCaseStarted).value(); - - const auto hasPickleStepId = [](const std::shared_ptr& testStep) - { - return testStep->pickleStepId.has_value(); - }; - const auto toLength = [this](const std::shared_ptr& testStep) - { - const auto pickleStep = query.FindPickleStepBy(testStep).value(); - const auto step = query.FindStepBy(pickleStep).value(); - return helper::Unstyled(helper::FormatStepTitle(*testStep, *pickleStep, *step, cucumber::messages::TestStepResultStatus::UNKNOWN, options.useStatusIcon, options.theme)).length(); - }; - - auto steplengths = testCase->testSteps | std::views::filter(hasPickleStepId) | std::views::transform(toLength); - - const auto maxStepLengthIter = std::ranges::max_element(steplengths); - const auto maxStepLength = (maxStepLengthIter != steplengths.end()) ? *maxStepLengthIter : 0; - - maxContentLengthByTestCaseStartedId[testCaseStarted->id] = std::max(scenarioLength, options.useStatusIcon ? maxStepLength + 2 : maxStepLength); - - std::size_t scenarioIndent{ 0 }; - if (options.includeFeatureLine) - { - scenarioIndent += helper::gherkinIndentLength; - if (options.includeRuleLine && lineage.rule) - scenarioIndent += helper::gherkinIndentLength; - } - - scenarioIndentByTestCaseStartedId[testCaseStarted->id] = scenarioIndent; - } - - void PrettyFormatter::HandleTestCaseStarted(const std::shared_ptr& testCaseStarted) - { - const auto pickle = query.FindPickleBy(testCaseStarted).value(); - const auto lineageAndPickle = query.FindLineageBy(pickle).value(); - const auto& lineage = *lineageAndPickle.lineage; - const auto& scenario = lineage.scenario; - const auto& rule = lineage.rule; - const auto& feature = lineage.feature; - - const auto scenarioIndent = scenarioIndentByTestCaseStartedId.at(testCaseStarted->id); - const auto maxContentLength = maxContentLengthByTestCaseStartedId.at(testCaseStarted->id); - - if (options.includeFeatureLine && rule && !printedFeatureUris.contains(feature.get())) - helper::PrintFeatureLine(outputStream, *feature, options.theme); - - if (options.includeRuleLine && rule && !printedRuleIds.contains(rule.get())) - helper::PrintRuleLine(outputStream, *rule, options.theme); - - outputStream << "\n"; - - helper::PrintTags(outputStream, *pickle, scenarioIndent, options.theme); - helper::PrintScenarioLine(outputStream, *pickle, *scenario, scenarioIndent, maxContentLength, options.theme); - - printedFeatureUris.insert(feature.get()); - printedRuleIds.insert(rule.get()); - } - - void PrettyFormatter::HandleAttachment(const cucumber::messages::Attachment& attachment) - { - if (!options.includeAttachments) - return; - - if (attachment.testCaseStartedId.has_value()) - helper::PrintAttachment(outputStream, attachment, scenarioIndentByTestCaseStartedId.at(attachment.testCaseStartedId.value()), options.useStatusIcon, options.theme); - } - - void PrettyFormatter::HandleTestStepFinished(const std::shared_ptr& testStepFinished) - { - const auto scenarioIndent = scenarioIndentByTestCaseStartedId.at(testStepFinished->testCaseStartedId); - const auto maxContentLength = maxContentLengthByTestCaseStartedId.at(testStepFinished->testCaseStartedId); - - const auto testStep = query.FindTestStepBy(testStepFinished).value(); - - if (const auto pickleStepOpt = query.FindPickleStepBy(testStep); pickleStepOpt.has_value()) - { - const auto& pickleStep = pickleStepOpt.value(); - const auto step = query.FindStepBy(pickleStep).value(); - const auto stepDefinitionOpt = query.FindUnambiguousStepDefinitionBy(testStep); - const auto* stepDefinition = stepDefinitionOpt.has_value() ? stepDefinitionOpt.value().get() : nullptr; - - helper::PrintStepLine(outputStream, *testStepFinished, *testStep, *pickleStep, *step, stepDefinition, scenarioIndent, maxContentLength, options.useStatusIcon, options.theme); - helper::PrintStepArgument(outputStream, *pickleStep, scenarioIndent, options.useStatusIcon, options.theme); - helper::PrintAmbiguousStep(outputStream, query, *testStepFinished, testStep, scenarioIndent, options.useStatusIcon, options.theme); - } - - helper::PrintError(outputStream, *testStepFinished, scenarioIndent, options.useStatusIcon, options.theme); - } - - void PrettyFormatter::HandleTestRunFinished(const cucumber::messages::TestRunFinished& testRunFinished) - { - const auto content = FormatTestRunFinishedError(testRunFinished, options.theme); - - if (!content.empty()) - fmt::println(outputStream, "{}", content); - } -} diff --git a/cucumber_cpp/library/formatter/PrettyFormatter.hpp b/cucumber_cpp/library/formatter/PrettyFormatter.hpp deleted file mode 100644 index 835eb1b6..00000000 --- a/cucumber_cpp/library/formatter/PrettyFormatter.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef FORMATTER_PRETTY_FORMATTER_HPP -#define FORMATTER_PRETTY_FORMATTER_HPP - -#include "cucumber/messages/Attachment.hpp" -#include "cucumber/messages/Envelope.hpp" -#include "cucumber/messages/Feature.hpp" -#include "cucumber/messages/Rule.hpp" -#include "cucumber/messages/TestCaseStarted.hpp" -#include "cucumber/messages/TestRunFinished.hpp" -#include "cucumber/messages/TestStepFinished.hpp" -#include "cucumber_cpp/library/formatter/Formatter.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "nlohmann/json.hpp" -#include "nlohmann/json_fwd.hpp" -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter -{ - struct PrettyFormatter - : Formatter - { - using Formatter::Formatter; - - constexpr static auto name = "pretty"; - - struct Options - { - explicit Options(const nlohmann::json& formatOptions); - - const bool includeAttachments; - const bool includeFeatureLine; - const bool includeRuleLine; - const bool useStatusIcon; - const helper::Theme theme; - }; - - private: - void OnEnvelope(const cucumber::messages::Envelope& envelope) override; - - void CalculateIndent(const std::shared_ptr& testCaseStarted); - - void HandleTestCaseStarted(const std::shared_ptr& testCaseStarted); - void HandleAttachment(const cucumber::messages::Attachment& attachment); - void HandleTestStepFinished(const std::shared_ptr& testStepFinished); - void HandleTestRunFinished(const cucumber::messages::TestRunFinished& testRunFinished); - - Options options{ formatOptions.contains(name) ? formatOptions.at(name) : nlohmann::json::object() }; - - std::map maxContentLengthByTestCaseStartedId; - std::map scenarioIndentByTestCaseStartedId; - - std::set printedFeatureUris; - std::set printedRuleIds; - }; -} - -#endif diff --git a/cucumber_cpp/library/formatter/SummaryFormatter.cpp b/cucumber_cpp/library/formatter/SummaryFormatter.cpp deleted file mode 100644 index eec0997e..00000000 --- a/cucumber_cpp/library/formatter/SummaryFormatter.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include "cucumber_cpp/library/formatter/SummaryFormatter.hpp" -#include "cucumber/messages/Duration.hpp" -#include "cucumber/messages/Envelope.hpp" -#include "cucumber/messages/Pickle.hpp" -#include "cucumber/messages/Scenario.hpp" -#include "cucumber/messages/TestCase.hpp" -#include "cucumber/messages/TestCaseFinished.hpp" -#include "cucumber/messages/TestCaseStarted.hpp" -#include "cucumber/messages/TestStep.hpp" -#include "cucumber/messages/TestStepFinished.hpp" -#include "cucumber/messages/TestStepResult.hpp" -#include "cucumber/messages/TestStepResultStatus.hpp" -#include "cucumber/query/Query.hpp" -#include "cucumber_cpp/library/formatter/helper/FormatMessages.hpp" -#include "cucumber_cpp/library/formatter/helper/PrintMessages.hpp" -#include "cucumber_cpp/library/formatter/helper/TextBuilder.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "cucumber_cpp/library/util/Duration.hpp" -#include "cucumber_cpp/library/util/ToLower.hpp" -#include "fmt/ostream.h" -#include "fmt/ranges.h" -#include "nlohmann/json.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter -{ - namespace - { - bool IsFailure(cucumber::messages::TestStepResultStatus status, bool willBeRetried) - { - return status == cucumber::messages::TestStepResultStatus::AMBIGUOUS || - status == cucumber::messages::TestStepResultStatus::UNDEFINED || - (status == cucumber::messages::TestStepResultStatus::FAILED && !willBeRetried); - } - - bool IsWarning(cucumber::messages::TestStepResultStatus status, bool willBeRetried) - { - return status == cucumber::messages::TestStepResultStatus::PENDING || - (status == cucumber::messages::TestStepResultStatus::FAILED && willBeRetried); - } - - struct SummaryData - { - std::map> warningTestStepResults; - std::map> failedTestStepResults; - std::map> scenarioCounts; - std::map> stepCounts; - cucumber::messages::Duration totalStepDuration{}; - }; - - void ClassifyTestCaseResult( - const std::string& id, - const std::shared_ptr& testCaseStarted, - const cucumber::messages::TestStepResult* testStepResult, - bool willBeRetried, - std::map>& warningResults, - std::map>& failedResults) - { - if (testStepResult == nullptr) - return; - - if (IsWarning(testStepResult->status, willBeRetried)) - warningResults[id] = testCaseStarted.get(); - - if (IsFailure(testStepResult->status, willBeRetried)) - failedResults[id] = testCaseStarted.get(); - } - - void CountScenarioResult( - const cucumber::query::Query& query, - const std::shared_ptr& testCaseFinished, - std::map>& scenarioCounts) - { - if (const auto testStepResultPtr = query.FindMostSevereTestStepResultBy(testCaseFinished); testStepResultPtr.has_value()) - ++scenarioCounts[testStepResultPtr.value()->status]; - else - ++scenarioCounts[cucumber::messages::TestStepResultStatus::PASSED]; - } - - void CountStepResults( - const cucumber::query::Query& query, - const std::shared_ptr& testCaseStarted, - std::map>& stepCounts, - cucumber::messages::Duration& totalStepDuration) - { - const auto testStepFinishedAndTestStep = query.FindTestStepFinishedAndTestStepBy(testCaseStarted); - for (const auto& [testStepFinished, testStep] : testStepFinishedAndTestStep) - { - if (testStep->pickleStepId.has_value()) - { - ++stepCounts[testStepFinished->testStepResult->status]; - if (testStepFinished->testStepResult->duration) - totalStepDuration += *testStepFinished->testStepResult->duration; - } - } - } - - SummaryData CollectSummaryData(const cucumber::query::Query& query) - { - SummaryData data; - - for (const auto& testCaseStarted : query.FindAllTestCaseStarted()) - { - const auto testCaseFinishedOpt = query.FindTestCaseFinishedBy(testCaseStarted); - if (!testCaseFinishedOpt.has_value()) - continue; - const auto& testCaseFinished = testCaseFinishedOpt.value(); - const auto mostSevereOpt = query.FindMostSevereTestStepResultBy(testCaseStarted); - const auto* testStepResult = mostSevereOpt.has_value() ? mostSevereOpt.value().get() : nullptr; - - ClassifyTestCaseResult(testCaseStarted->id, testCaseStarted, testStepResult, testCaseFinished->willBeRetried, data.warningTestStepResults, data.failedTestStepResults); - - if (!testCaseFinished->willBeRetried) - { - CountScenarioResult(query, testCaseFinished, data.scenarioCounts); - CountStepResults(query, testCaseStarted, data.stepCounts, data.totalStepDuration); - } - } - - return data; - } - - std::size_t CalculateLength(const cucumber::query::Query& query, const cucumber::messages::Pickle& pickle, const cucumber::messages::TestCaseStarted& testCaseStarted, const cucumber::messages::TestCaseFinished& testCaseFinished, const cucumber::messages::Scenario& scenario, const cucumber::messages::TestCase& testCase, bool useStatusIcon, const helper::Theme& theme) // NOSONAR: cohesive formatting helper - { - const auto scenarioLength = helper::Unstyled(helper::FormatPickleAttemptTitle(pickle, testCaseStarted.attempt, testCaseFinished.willBeRetried, scenario, theme)).length(); - - const auto toLength = [&query, useStatusIcon, &theme, isBeforeHook = true](const std::shared_ptr& testStep) mutable -> std::size_t - { - if (testStep->hookId.has_value()) - { - const auto hookOpt = query.FindHookBy(testStep); - if (!hookOpt.has_value()) - return 0; - return helper::Unstyled(helper::FormatHookTitle(*hookOpt.value(), cucumber::messages::TestStepResultStatus::UNKNOWN, isBeforeHook, useStatusIcon, theme)).length(); - } - else if (testStep->pickleStepId.has_value()) - { - isBeforeHook = false; - - const auto pickleStep = query.FindPickleStepBy(testStep).value(); - const auto step = query.FindStepBy(pickleStep).value(); - return helper::Unstyled(helper::FormatStepTitle(*testStep, *pickleStep, *step, cucumber::messages::TestStepResultStatus::UNKNOWN, useStatusIcon, theme)).length(); - } - return 0; - }; - - auto steplengths = testCase.testSteps | std::views::transform(toLength); - - const auto maxStepLengthIter = std::ranges::max_element(steplengths); - const auto maxStepLength = (maxStepLengthIter != steplengths.end()) ? *maxStepLengthIter : 0; - const auto maxContentLength = std::max(scenarioLength, useStatusIcon ? maxStepLength + 2 : maxStepLength); - - return maxContentLength; - } - - void HandleHookStep(std::ostream& stream, const cucumber::query::Query& query, const std::shared_ptr& testStepFinished, const std::shared_ptr& testStep, std::size_t scenarioIndent, std::size_t maxContentLength, bool isBeforeHook, bool useStatusIcon, const helper::Theme& theme) // NOSONAR: cohesive formatting helper - { - if (const auto hookOpt = query.FindHookBy(testStep); hookOpt.has_value()) - helper::PrintHookLine(stream, *testStepFinished, *hookOpt.value(), scenarioIndent, maxContentLength, isBeforeHook, useStatusIcon, theme); - - helper::PrintError(stream, *testStepFinished, scenarioIndent, useStatusIcon, theme); - } - - void HandleTestStep(std::ostream& stream, const cucumber::query::Query& query, const std::shared_ptr& testStepFinished, const std::shared_ptr& testStep, std::size_t scenarioIndent, std::size_t maxContentLength, bool useStatusIcon, const helper::Theme& theme) // NOSONAR: cohesive formatting helper - { - const auto pickleStep = query.FindPickleStepBy(testStep).value(); - const auto step = query.FindStepBy(pickleStep).value(); - const auto stepDefinitionOpt = query.FindUnambiguousStepDefinitionBy(testStep); - const auto* stepDefinition = stepDefinitionOpt.has_value() ? stepDefinitionOpt.value().get() : nullptr; - - helper::PrintStepLine(stream, *testStepFinished, *testStep, *pickleStep, *step, stepDefinition, scenarioIndent, maxContentLength, useStatusIcon, theme); - - helper::PrintStepArgument(stream, *pickleStep, scenarioIndent, useStatusIcon, theme); - helper::PrintAmbiguousStep(stream, query, *testStepFinished, testStep, scenarioIndent, useStatusIcon, theme); - - helper::PrintError(stream, *testStepFinished, scenarioIndent, useStatusIcon, theme); - } - - void HandleTestSteps(std::ostream& stream, const cucumber::query::Query& query, const std::shared_ptr& testCaseStarted, std::size_t scenarioIndent, std::size_t maxContentLength, bool useStatusIcon, const helper::Theme& theme) - { - const auto testStepFinishedAndTestStep = query.FindTestStepFinishedAndTestStepBy(testCaseStarted); - - auto isBeforeHook = true; - for (const auto& [testStepFinished, testStep] : testStepFinishedAndTestStep) - { - if (testStep->hookId.has_value()) - HandleHookStep(stream, query, testStepFinished, testStep, scenarioIndent, maxContentLength, isBeforeHook, useStatusIcon, theme); - else - { - isBeforeHook = false; - HandleTestStep(stream, query, testStepFinished, testStep, scenarioIndent, maxContentLength, useStatusIcon, theme); - } - } - } - - void HandleTestCaseStarted(std::ostream& stream, const cucumber::query::Query& query, const std::shared_ptr& testCaseStarted, bool useStatusIcon, const helper::Theme& theme) - { - auto scenarioIndent = 0; - - const auto testCaseFinished = query.FindTestCaseFinishedBy(testCaseStarted).value(); - const auto pickle = query.FindPickleBy(testCaseStarted).value(); - const auto lineageAndPickle = query.FindLineageBy(pickle).value(); - const auto& lineage = *lineageAndPickle.lineage; - const auto& scenario = lineage.scenario; - const auto testCase = query.FindTestCaseBy(testCaseStarted).value(); - - const auto maxContentLength = CalculateLength(query, *pickle, *testCaseStarted, *testCaseFinished, *scenario, *testCase, useStatusIcon, theme); - - fmt::println(stream, ""); - helper::PrintScenarioAttemptLine(stream, *pickle, testCaseStarted->attempt, testCaseFinished->willBeRetried, *scenario, scenarioIndent, maxContentLength, theme); - HandleTestSteps(stream, query, testCaseStarted, scenarioIndent, maxContentLength, useStatusIcon, theme); - } - - void HandleTestCaseStartedList(std::ostream& stream, const cucumber::query::Query& query, const std::string& title, const std::map>& testCaseStartedList, bool useStatusIcon, const helper::Theme& theme) - { - if (testCaseStartedList.empty()) - return; - - fmt::println(stream, "\n{}:", title); - - for (const auto& [id, testCaseStarted] : testCaseStartedList) - HandleTestCaseStarted(stream, query, std::shared_ptr{ std::shared_ptr{}, testCaseStarted }, useStatusIcon, theme); - } - - void HandleSummary(std::ostream& stream, const std::string& summary, const std::map>& counts, const helper::Theme& theme) - { - const auto countToStatusString = [&theme](const auto& pair) - { - const auto& [status, count] = pair; - return helper::TextBuilder{} - .Append(std::to_string(count)) - .Space() - .Append(util::ToLower(std::string{ cucumber::messages::to_string(status) })) - .Build(theme.status.All(status)); - }; - - auto countsValues = counts | std::views::values; - const auto totalCount = std::accumulate(countsValues.begin(), countsValues.end(), std::size_t{ 0u }); - - fmt::println(stream, "{} {} {}", totalCount, summary, - fmt::join(counts | std::views::transform(countToStatusString), ", ")); - } - } - - SummaryFormatter::Options::Options(const nlohmann::json& formatOptions) - : useStatusIcon{ formatOptions.value("use_status_icon", true) } - , theme{ helper::CreateTheme(formatOptions.value("theme", std::string_view{ "cucumber" })) } - { - } - - void SummaryFormatter::OnEnvelope(const cucumber::messages::Envelope& envelope) - { - if (envelope.testRunFinished) - { - const auto testRunDuration = query.FindTestRunDuration(); - LogSummary(testRunDuration.has_value() ? *testRunDuration.value() : cucumber::messages::Duration{}); - } - } - - void SummaryFormatter::LogSummary(const cucumber::messages::Duration& testRunDuration) - { - const auto data = CollectSummaryData(query); - - HandleTestCaseStartedList(outputStream, query, "Warnings", data.warningTestStepResults, options.useStatusIcon, options.theme); - HandleTestCaseStartedList(outputStream, query, "Failures", data.failedTestStepResults, options.useStatusIcon, options.theme); - HandleSummary(outputStream, "scenarios", data.scenarioCounts, options.theme); - HandleSummary(outputStream, "steps", data.stepCounts, options.theme); - - fmt::println(outputStream, "{:%Mm %S}s (executing steps: {:%Mm %S}s)", util::DurationToMilliseconds(testRunDuration), util::DurationToMilliseconds(data.totalStepDuration)); - } -} diff --git a/cucumber_cpp/library/formatter/SummaryFormatter.hpp b/cucumber_cpp/library/formatter/SummaryFormatter.hpp deleted file mode 100644 index a3e56b45..00000000 --- a/cucumber_cpp/library/formatter/SummaryFormatter.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef FORMATTER_SUMMARY_FORMATTER_HPP -#define FORMATTER_SUMMARY_FORMATTER_HPP - -#include "cucumber/messages/Duration.hpp" -#include "cucumber/messages/Envelope.hpp" -#include "cucumber_cpp/library/formatter/Formatter.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "nlohmann/json.hpp" -#include - -namespace cucumber_cpp::library::formatter -{ - struct SummaryFormatter - : Formatter - { - using Formatter::Formatter; - - constexpr static auto name = "summary"; - - private: - struct Options - { - explicit Options(const nlohmann::json& formatOptions); - - const bool useStatusIcon; - const helper::Theme theme; - }; - - void OnEnvelope(const cucumber::messages::Envelope& envelope) override; - void LogSummary(const cucumber::messages::Duration& testRunDuration); - - Options options{ formatOptions.contains(name) ? formatOptions.at(name) : nlohmann::json::object() }; - }; -} - -#endif diff --git a/cucumber_cpp/library/formatter/UsageFormatter.cpp b/cucumber_cpp/library/formatter/UsageFormatter.cpp index eefbe569..25cda9d9 100644 --- a/cucumber_cpp/library/formatter/UsageFormatter.cpp +++ b/cucumber_cpp/library/formatter/UsageFormatter.cpp @@ -8,7 +8,7 @@ #include "cucumber/query/Query.hpp" #include "cucumber_cpp/library/formatter/helper/Theme.hpp" #include "cucumber_cpp/library/util/Duration.hpp" -#include "fmt/base.h" +#include "fmt/core.h" #include "fmt/format.h" #include "fmt/ostream.h" #include "nlohmann/json.hpp" @@ -64,12 +64,12 @@ namespace cucumber_cpp::library::formatter std::map> mapping; for (const auto& stepDefinition : query.FindAllStepDefinitions()) - mapping[stepDefinition->id] = Usage{ - .line = (stepDefinition->sourceReference && stepDefinition->sourceReference->location) ? (*stepDefinition->sourceReference->location)->line : 0, + mapping[stepDefinition.id] = Usage{ + .line = stepDefinition.sourceReference.location ? stepDefinition.sourceReference.location->line : 0, .matches = {}, - .pattern = stepDefinition->pattern->source, - .patternType = std::string{ cucumber::messages::to_string(stepDefinition->pattern->type) }, - .uri = stepDefinition->sourceReference ? stepDefinition->sourceReference->uri.value_or("") : "", + .pattern = stepDefinition.pattern.source, + .patternType = std::string{ cucumber::messages::to_string(stepDefinition.pattern.type) }, + .uri = stepDefinition.sourceReference.uri.value_or(""), .meanDuration = {}, }; @@ -83,20 +83,20 @@ namespace cucumber_cpp::library::formatter UsageMatch CreateUsageMatch( const cucumber::query::Query& query, - const std::shared_ptr& testStepFinished, - const std::shared_ptr& testStep, + const cucumber::messages::TestStepFinished* testStepFinished, + const cucumber::messages::TestStep* testStep, const cucumber::query::Lineage& lineage) { - const auto pickleStep = query.FindPickleStepBy(testStep).value(); - const auto step = query.FindStepBy(pickleStep).value(); + const auto* pickleStep = query.FindPickleStepBy(*testStep); + const auto* step = query.FindStepBy(*pickleStep); std::optional duration{}; - if (HasExecuted(testStepFinished->testStepResult->status) && testStepFinished->testStepResult->duration) - duration = util::DurationToNanoSeconds(*testStepFinished->testStepResult->duration); + if (HasExecuted(testStepFinished->testStepResult.status)) + duration = util::DurationToNanoSeconds(testStepFinished->testStepResult.duration); return UsageMatch{ .duration = duration, - .line = step->location->line, + .line = step->location.line, .text = pickleStep->text, .uri = lineage.gherkinDocument->uri.value_or("") }; @@ -104,8 +104,8 @@ namespace cucumber_cpp::library::formatter void AddUsageMatchToMapping( const cucumber::query::Query& query, - const std::shared_ptr& testStepFinished, - const std::shared_ptr& testStep, + const cucumber::messages::TestStepFinished* testStepFinished, + const cucumber::messages::TestStep* testStep, const cucumber::query::Lineage& lineage, std::map>& mapping) { @@ -144,7 +144,7 @@ namespace cucumber_cpp::library::formatter { for (const auto& testCaseStarted : query.FindAllTestCaseStarted()) { - if (!query.FindTestCaseFinishedBy(testCaseStarted).has_value()) + if (query.FindTestCaseFinishedBy(testCaseStarted) == nullptr) continue; const auto lineageAndPickle = query.FindLineageBy(testCaseStarted).value(); @@ -225,7 +225,7 @@ namespace cucumber_cpp::library::formatter std::string FormatDuration(const std::optional& duration) { if (duration.has_value()) - return fmt::format("{}", std::chrono::duration_cast(duration.value())); + return fmt::format("{}", std::chrono::duration_cast(duration.value()).count()); return "-"; } diff --git a/cucumber_cpp/library/formatter/UsageFormatter.hpp b/cucumber_cpp/library/formatter/UsageFormatter.hpp index df55d1c1..b5c599a5 100644 --- a/cucumber_cpp/library/formatter/UsageFormatter.hpp +++ b/cucumber_cpp/library/formatter/UsageFormatter.hpp @@ -4,7 +4,7 @@ #include "cucumber/messages/Envelope.hpp" #include "cucumber_cpp/library/formatter/Formatter.hpp" #include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "fmt/base.h" +#include "fmt/core.h" #include "fmt/format.h" #include "nlohmann/json.hpp" #include diff --git a/cucumber_cpp/library/formatter/helper/CMakeLists.txt b/cucumber_cpp/library/formatter/helper/CMakeLists.txt index de2459a8..5519e4a7 100644 --- a/cucumber_cpp/library/formatter/helper/CMakeLists.txt +++ b/cucumber_cpp/library/formatter/helper/CMakeLists.txt @@ -1,14 +1,6 @@ add_library(cucumber_cpp.library.formatter.helper ${CCR_EXCLUDE_FROM_ALL}) target_sources(cucumber_cpp.library.formatter.helper PRIVATE - FormatMessages.cpp - FormatMessages.hpp - IndentString.cpp - IndentString.hpp - PrintMessages.cpp - PrintMessages.hpp - TextBuilder.cpp - TextBuilder.hpp Theme.cpp Theme.hpp ) @@ -18,7 +10,6 @@ target_include_directories(cucumber_cpp.library.formatter.helper PUBLIC ) target_link_libraries(cucumber_cpp.library.formatter.helper PUBLIC - cucumber_cpp.library.cucumber_expression cucumber_cpp.library.support cucumber::query fmt::fmt diff --git a/cucumber_cpp/library/formatter/helper/FormatMessages.cpp b/cucumber_cpp/library/formatter/helper/FormatMessages.cpp deleted file mode 100644 index 3a07cdcc..00000000 --- a/cucumber_cpp/library/formatter/helper/FormatMessages.cpp +++ /dev/null @@ -1,375 +0,0 @@ -#include "cucumber_cpp/library/formatter/helper/FormatMessages.hpp" -#include "cucumber/messages/Attachment.hpp" -#include "cucumber/messages/AttachmentContentEncoding.hpp" -#include "cucumber/messages/Feature.hpp" -#include "cucumber/messages/Hook.hpp" -#include "cucumber/messages/Location.hpp" -#include "cucumber/messages/Pickle.hpp" -#include "cucumber/messages/PickleDocString.hpp" -#include "cucumber/messages/PickleStep.hpp" -#include "cucumber/messages/PickleTable.hpp" -#include "cucumber/messages/Rule.hpp" -#include "cucumber/messages/Scenario.hpp" -#include "cucumber/messages/SourceReference.hpp" -#include "cucumber/messages/Step.hpp" -#include "cucumber/messages/StepDefinition.hpp" -#include "cucumber/messages/TestRunFinished.hpp" -#include "cucumber/messages/TestStep.hpp" -#include "cucumber/messages/TestStepResult.hpp" -#include "cucumber/messages/TestStepResultStatus.hpp" -#include "cucumber_cpp/library/formatter/helper/TextBuilder.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "cucumber_cpp/library/util/Trim.hpp" -#include "fmt/color.h" -#include "fmt/format.h" -#include "fmt/ranges.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - namespace - { - std::vector CalculateColumnWidths(const cucumber::messages::PickleTable& pickleDataTable) - { - std::vector columnWidths(pickleDataTable.rows.empty() ? 0 : pickleDataTable.rows.front()->cells.size(), 0); - - for (const auto& row : pickleDataTable.rows) - for (std::size_t colIndex = 0; colIndex < row->cells.size(); ++colIndex) - { - const auto cellContentLength = row->cells[colIndex]->value.length(); - columnWidths[colIndex] = std::max(columnWidths[colIndex], cellContentLength); - } - - return columnWidths; - } - - const auto transformToString = [](auto subrange) - { - return std::string_view{ subrange.begin(), subrange.end() }; - }; - - std::string GetAttemptText(std::size_t attempt, bool willBeRetried) - { - if (attempt > 0 || willBeRetried) - return fmt::format("(attempt {}{})", attempt + 1, willBeRetried ? ", retried" : ""); - return ""; - } - } - - std::string FormatPickleTitle(const cucumber::messages::Pickle& pickle, const cucumber::messages::Scenario& scenario, const Theme& theme) - { - return TextBuilder{} - .Append(scenario.keyword + ":", theme.scenario.keyword) - .Space() - .Append(pickle.name, theme.scenario.name) - .Build(theme.scenario.all); - } - - std::string FormatPickleAttemptTitle(const cucumber::messages::Pickle& pickle, std::size_t attempt, bool retry, const cucumber::messages::Scenario& scenario, const Theme& theme) - { - auto attemptText = GetAttemptText(attempt, retry); - - TextBuilder builder{}; - builder.Append(scenario.keyword + ":", theme.scenario.keyword) - .Space() - .Append(pickle.name, theme.scenario.name); - - if (!attemptText.empty()) - builder.Space().Append(attemptText, theme.scenario.attempt); - - return builder.Build(theme.scenario.all); - } - - std::string FormatPickleLocation(const cucumber::messages::Pickle& pickle, const std::shared_ptr& location, const Theme& theme) - { - TextBuilder builder{}; - - builder.Append("#") - .Space() - .Append(pickle.uri); - if (location) - builder.Append(":") - .Append(std::to_string(location->line)); - - return builder.Build(theme.location); - } - - std::string FormatStepText(const cucumber::messages::TestStep& testStep, const cucumber::messages::PickleStep& pickleStep, cucumber::messages::TestStepResultStatus status, const Theme& theme) - { - TextBuilder builder{}; - const auto& stepMatchArgumentsLists = testStep.stepMatchArgumentsLists; - - if (stepMatchArgumentsLists && stepMatchArgumentsLists->size() == 1) - { - const auto& stepMatchArguments = stepMatchArgumentsLists->front()->stepMatchArguments; - std::size_t currentIndex = 0; - - for (const auto& argument : stepMatchArguments) - { - const auto& group = *argument->group; - - if (group.value.has_value() && group.start.has_value()) - { - const auto text = pickleStep.text.substr(currentIndex, group.start.value() - currentIndex); - currentIndex = group.start.value() + group.value->size(); - builder.Append(text, theme.step.text.value_or(fmt::text_style{}) | theme.status.All(status)) - .Append(group.value.value(), theme.step.argument.value_or(fmt::text_style{}) | theme.status.All(status)); - } - } - if (currentIndex != pickleStep.text.size()) - { - const auto remainingText = pickleStep.text.substr(currentIndex); - builder.Append(remainingText, theme.step.text.value_or(fmt::text_style{}) | theme.status.All(status)); - } - } - else - builder.Append(pickleStep.text, theme.step.text.value_or(fmt::text_style{}) | theme.status.All(status)); - - return builder.Build(); - } - - std::string FormatCodeLocation(const cucumber::messages::SourceReference& sourceReference, const Theme& theme) - { - if (sourceReference.uri.has_value()) - { - TextBuilder builder{}; - - builder.Append("#") - .Space() - .Append(sourceReference.uri.value()); - - if (sourceReference.location.has_value()) - builder.Append(":") - .Append(std::to_string(sourceReference.location.value()->line)); - return builder.Build(theme.location); - } - - return ""; - } - - std::string FormatCodeLocation(const cucumber::messages::StepDefinition* stepDefinition, const Theme& theme) - { - if (stepDefinition != nullptr) - return FormatCodeLocation(*stepDefinition->sourceReference, theme); - - return ""; - } - - std::string FormatFeatureTitle(const cucumber::messages::Feature& feature, const Theme& theme) - { - return TextBuilder{} - .Append(feature.keyword + ":", theme.feature.keyword) - .Space() - .Append(feature.name, theme.feature.name) - .Build(theme.feature.all); - } - - std::string FormatRuleTitle(const cucumber::messages::Rule& rule, const Theme& theme) - { - return TextBuilder{} - .Append(rule.keyword + ":", theme.rule.keyword) - .Space() - .Append(rule.name, theme.rule.name) - .Build(theme.rule.all); - } - - std::string FormatPickleTags(const cucumber::messages::Pickle& pickle, const Theme& theme) - { - if (!pickle.tags.empty()) - { - return TextBuilder{} - .Append(fmt::to_string(fmt::join(pickle.tags | std::views::transform([](const auto& tag) - { - return tag->name; - }), - " "))) - .Build(theme.tag); - } - return ""; - } - - std::string FormatHookTitle(const cucumber::messages::Hook& hook, cucumber::messages::TestStepResultStatus status, bool isBeforeHook, bool useStatusIcon, const Theme& theme) - { - TextBuilder builder{}; - - if (useStatusIcon) - builder.Append(theme.status.Icon(status, " "), theme.status.All(status)).Space(); - - builder.Append((isBeforeHook ? "Before" : "After"), theme.step.keyword.value_or(fmt::text_style{}) | theme.status.All(status)); - - if (hook.name.has_value()) - builder.Space().Append('(' + hook.name.value() + ')', theme.status.All(status)); - - return builder.Build(theme.status.All(status)); - } - - std::string FormatStepTitle(const cucumber::messages::TestStep& testStep, const cucumber::messages::PickleStep& pickleStep, const cucumber::messages::Step& step, cucumber::messages::TestStepResultStatus status, bool useStatusIcon, const Theme& theme) - { - auto builder = TextBuilder{}; - if (useStatusIcon) - builder.Append(theme.status.Icon(status, " "), theme.status.All(status)).Space(); - - return builder.Append(TextBuilder{} - .Append(step.keyword, theme.step.keyword) - .Append(FormatStepText(testStep, pickleStep, status, theme), theme.status.All(status)) - .Build(theme.status.All(status))) - .Build(); - } - - std::string FormatDocString(const cucumber::messages::PickleDocString& pickleDocString, const Theme& theme) - { - TextBuilder builder{}; - builder.Append(R"(""")", theme.docString.delimiter); - if (pickleDocString.mediaType.has_value()) - builder.Append(pickleDocString.mediaType.value(), theme.docString.mediaType); - builder.Line(); - - for (const auto& line : pickleDocString.content | std::views::split('\n') | std::views::transform(transformToString)) - builder.Append(line).Line(); - - builder.Append(R"(""")", theme.docString.delimiter); - - return builder.Build(theme.docString.all, true); - } - - std::string FormatDataTable(const cucumber::messages::PickleTable& pickleDataTable, const Theme& theme) - { - const auto columnWidths = CalculateColumnWidths(pickleDataTable); - TextBuilder builder{}; - - for (auto rowIndex = 0; rowIndex != pickleDataTable.rows.size(); ++rowIndex) - { - const auto& row = pickleDataTable.rows[rowIndex]; - - if (rowIndex > 0) - builder.Line(); - builder.Append("|", theme.dataTable.border); - - for (auto colIndex = 0; colIndex != pickleDataTable.rows[rowIndex]->cells.size(); ++colIndex) - { - const auto& cell = row->cells[colIndex]; - builder.Append(fmt::format(" {:<{}} ", cell->value, columnWidths[colIndex]), theme.dataTable.content) - .Append("|", theme.dataTable.border); - } - } - - return builder.Build(theme.dataTable.all, true); - } - - std::string FormatPickleStepArgument(const cucumber::messages::PickleStep& pickleStep, const Theme& theme) - { - if (pickleStep.argument && (*pickleStep.argument)->docString.has_value()) - return FormatDocString(*(*pickleStep.argument)->docString.value(), theme); - - if (pickleStep.argument && (*pickleStep.argument)->dataTable.has_value()) - return FormatDataTable(*(*pickleStep.argument)->dataTable.value(), theme); - - return ""; - } - - std::string FormatAmbiguousStep(const std::list& stepDefinitions, const Theme& theme) - { - TextBuilder builder{}; - builder.Append("Multiple matching step definitions found:"); - for (const auto* stepDefinition : stepDefinitions) - { - builder.Line().Append(" " + theme.symbol.bullet + " "); - - if (!stepDefinition->pattern->source.empty()) - builder.Append(stepDefinition->pattern->source); - - const auto location = FormatCodeLocation(stepDefinition, theme); - if (!location.empty()) - builder.Space().Append(location); - } - return builder.Build({}, true); - } - - std::string FormatTestStepResultError(const cucumber::messages::TestStepResult& testStepResult, const Theme& theme) - { - if (testStepResult.exception.has_value() && testStepResult.exception.value()->stackTrace.has_value()) - { - return TextBuilder{} - .Append(util::Trim(testStepResult.exception.value()->stackTrace.value())) - .Build(theme.status.All(testStepResult.status), true); - } - - if (testStepResult.exception.has_value() && testStepResult.exception.value()->message.has_value()) - { - return TextBuilder{} - .Append(util::Trim(testStepResult.exception.value()->type)) - .Append(": ") - .Append(util::Trim(testStepResult.exception.value()->message.value())) - .Build(theme.status.All(testStepResult.status), true); - } - - if (testStepResult.message.has_value()) - { - return TextBuilder{} - .Append(util::Trim(testStepResult.message.value())) - .Build(theme.status.All(testStepResult.status), true); - } - - return ""; - } - - std::string FormatTestRunFinishedError(const cucumber::messages::TestRunFinished& testRunFinished, const Theme& theme) - { - if (testRunFinished.exception && (*testRunFinished.exception)->stackTrace) - { - return TextBuilder{} - .Append(util::Trim((*testRunFinished.exception)->stackTrace.value())) - .Build(theme.status.All(cucumber::messages::TestStepResultStatus::FAILED)); - } - - if (testRunFinished.exception && (*testRunFinished.exception)->message) - { - return TextBuilder{} - .Append(util::Trim((*testRunFinished.exception)->message.value())) - .Build(theme.status.All(cucumber::messages::TestStepResultStatus::FAILED)); - } - - return ""; - } - - std::string FormatBase64Attachment(std::string_view body, const std::string& mediaType, const std::optional& filename, const Theme& theme) - { - TextBuilder builder{}; - builder.Append("Embedding").Space(); - - if (filename) - builder.Append(filename.value()).Space(); - - builder - .Append("[") - .Append(mediaType) - .Space() - .Append(std::to_string(body.length() / 4 * 3)) - .Space() - .Append("bytes]"); - - return builder.Build(theme.attachment); - } - - std::string FormatTextAttachment(const std::string& body, const Theme& theme) - { - return TextBuilder{}.Append(body).Build(theme.attachment); - } - - std::string FormatAttachment(const cucumber::messages::Attachment& attachment, const Theme& theme) - { - if (attachment.contentEncoding == cucumber::messages::AttachmentContentEncoding::BASE64) - return FormatBase64Attachment(attachment.body, attachment.mediaType, attachment.fileName, theme); - else - return FormatTextAttachment(attachment.body, theme); - return ""; - } -} diff --git a/cucumber_cpp/library/formatter/helper/FormatMessages.hpp b/cucumber_cpp/library/formatter/helper/FormatMessages.hpp deleted file mode 100644 index fe1cc44f..00000000 --- a/cucumber_cpp/library/formatter/helper/FormatMessages.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef HELPER_FORMAT_MESSAGES_HPP -#define HELPER_FORMAT_MESSAGES_HPP - -#include "cucumber/messages/Attachment.hpp" -#include "cucumber/messages/Feature.hpp" -#include "cucumber/messages/Hook.hpp" -#include "cucumber/messages/Location.hpp" -#include "cucumber/messages/Pickle.hpp" -#include "cucumber/messages/PickleDocString.hpp" -#include "cucumber/messages/PickleStep.hpp" -#include "cucumber/messages/PickleTable.hpp" -#include "cucumber/messages/Rule.hpp" -#include "cucumber/messages/Scenario.hpp" -#include "cucumber/messages/SourceReference.hpp" -#include "cucumber/messages/Step.hpp" -#include "cucumber/messages/StepDefinition.hpp" -#include "cucumber/messages/TestRunFinished.hpp" -#include "cucumber/messages/TestStep.hpp" -#include "cucumber/messages/TestStepResult.hpp" -#include "cucumber/messages/TestStepResultStatus.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - std::string FormatPickleTitle(const cucumber::messages::Pickle& pickle, const cucumber::messages::Scenario& scenario, const Theme& theme); - std::string FormatPickleAttemptTitle(const cucumber::messages::Pickle& pickle, std::size_t attempt, bool retry, const cucumber::messages::Scenario& scenario, const Theme& theme); - std::string FormatPickleLocation(const cucumber::messages::Pickle& pickle, const std::shared_ptr& location, const Theme& theme); - std::string FormatStepText(const cucumber::messages::TestStep& testStep, const cucumber::messages::PickleStep& pickleStep, cucumber::messages::TestStepResultStatus status, const Theme& theme); - std::string FormatCodeLocation(const cucumber::messages::SourceReference& sourceReference, const Theme& theme); - std::string FormatCodeLocation(const cucumber::messages::StepDefinition* stepDefinition, const Theme& theme); - std::string FormatFeatureTitle(const cucumber::messages::Feature& feature, const Theme& theme); - std::string FormatRuleTitle(const cucumber::messages::Rule& rule, const Theme& theme); - std::string FormatPickleTags(const cucumber::messages::Pickle& pickle, const Theme& theme); - std::string FormatHookTitle(const cucumber::messages::Hook& hook, cucumber::messages::TestStepResultStatus status, bool isBeforeHook, bool useStatusIcon, const Theme& theme); - std::string FormatStepTitle(const cucumber::messages::TestStep& testStep, const cucumber::messages::PickleStep& pickleStep, const cucumber::messages::Step& step, cucumber::messages::TestStepResultStatus status, bool useStatusIcon, const Theme& theme); - std::string FormatDocString(const cucumber::messages::PickleDocString& pickleDocString, const Theme& theme); - std::string FormatDataTable(const cucumber::messages::PickleTable& pickleDataTable, const Theme& theme); - std::string FormatPickleStepArgument(const cucumber::messages::PickleStep& pickleStep, const Theme& theme); - std::string FormatAmbiguousStep(const std::list& stepDefinitions, const Theme& theme); - std::string FormatTestStepResultError(const cucumber::messages::TestStepResult& testStepResult, const Theme& theme); - std::string FormatTestRunFinishedError(const cucumber::messages::TestRunFinished& testRunFinished, const Theme& theme); - std::string FormatBase64Attachment(std::string_view body, const std::string& mediaType, const std::optional& filename, const Theme& theme); - std::string FormatTextAttachment(const std::string& body, const Theme& theme); - std::string FormatAttachment(const cucumber::messages::Attachment& attachment, const Theme& theme); -} - -#endif diff --git a/cucumber_cpp/library/formatter/helper/IndentString.cpp b/cucumber_cpp/library/formatter/helper/IndentString.cpp deleted file mode 100644 index f1f48221..00000000 --- a/cucumber_cpp/library/formatter/helper/IndentString.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "cucumber_cpp/library/formatter/helper/IndentString.hpp" -#include "fmt/format.h" -#include "fmt/ranges.h" -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - std::string IndentString(const std::string& str, std::size_t indentSize) - { - auto lines = str | - std::views::split('\n') | - std::views::transform([indent = std::string(indentSize, ' ')](const auto& line) - { - return indent + std::string{ line.begin(), line.end() }; - }); - - return fmt::to_string(fmt::join(lines, "\n")); - } -} diff --git a/cucumber_cpp/library/formatter/helper/IndentString.hpp b/cucumber_cpp/library/formatter/helper/IndentString.hpp deleted file mode 100644 index 3ae582c9..00000000 --- a/cucumber_cpp/library/formatter/helper/IndentString.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef HELPER_INDENT_STRING_HPP -#define HELPER_INDENT_STRING_HPP - -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - std::string IndentString(const std::string& str, std::size_t indentSize); -} - -#endif diff --git a/cucumber_cpp/library/formatter/helper/PrintMessages.cpp b/cucumber_cpp/library/formatter/helper/PrintMessages.cpp deleted file mode 100644 index d92d1752..00000000 --- a/cucumber_cpp/library/formatter/helper/PrintMessages.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "cucumber_cpp/library/formatter/helper/PrintMessages.hpp" -#include "cucumber/messages/Attachment.hpp" -#include "cucumber/messages/Feature.hpp" -#include "cucumber/messages/Hook.hpp" -#include "cucumber/messages/Pickle.hpp" -#include "cucumber/messages/PickleStep.hpp" -#include "cucumber/messages/Rule.hpp" -#include "cucumber/messages/Scenario.hpp" -#include "cucumber/messages/Step.hpp" -#include "cucumber/messages/StepDefinition.hpp" -#include "cucumber/messages/TestStep.hpp" -#include "cucumber/messages/TestStepFinished.hpp" -#include "cucumber/messages/TestStepResultStatus.hpp" -#include "cucumber/query/Query.hpp" -#include "cucumber_cpp/library/formatter/helper/FormatMessages.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include "fmt/format.h" -#include "fmt/ostream.h" -#include "fmt/ranges.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - namespace - { - const auto transformToString = [](auto subrange) - { - return std::string_view{ subrange.begin(), subrange.end() }; - }; - - void PrintlnIndentedContent(std::ostream& os, std::string_view content, std::size_t indent) - { - const std::string indentStr(indent, ' '); - fmt::println(os, "{}{}", indentStr, fmt::join(content | std::views::split('\n') | std::views::transform(transformToString), "\n" + indentStr)); - } - } - - void PrintFeatureLine(std::ostream& stream, const cucumber::messages::Feature& feature, const Theme& theme) - { - fmt::println(stream, "\n{}", FormatFeatureTitle(feature, theme)); - } - - void PrintRuleLine(std::ostream& stream, const cucumber::messages::Rule& rule, const Theme& theme) - { - fmt::println(stream, "\n{}{}", std::string(gherkinIndentLength, ' '), FormatRuleTitle(rule, theme)); - } - - void PrintTags(std::ostream& stream, const cucumber::messages::Pickle& pickle, std::size_t scenarioIndent, const Theme& theme) - { - if (pickle.tags.empty()) - return; - - fmt::println(stream, "{}{}", std::string(scenarioIndent, ' '), FormatPickleTags(pickle, theme)); - } - - void PrintScenarioLine(std::ostream& stream, const cucumber::messages::Pickle& pickle, const cucumber::messages::Scenario& scenario, std::size_t scenarioIndent, std::size_t maxContentLength, const Theme& theme) - { - PrintGherkinLine(stream, - FormatPickleTitle(pickle, scenario, theme), - FormatPickleLocation(pickle, scenario.location, theme), - scenarioIndent, - maxContentLength, theme); - } - - void PrintScenarioAttemptLine(std::ostream& stream, const cucumber::messages::Pickle& pickle, std::size_t attempt, bool retry, const cucumber::messages::Scenario& scenario, std::size_t scenarioIndent, std::size_t maxContentLength, const Theme& theme) // NOSONAR: cohesive print helper - { - PrintGherkinLine(stream, - FormatPickleAttemptTitle(pickle, attempt, retry, scenario, theme), - FormatPickleLocation(pickle, scenario.location, theme), - scenarioIndent, - maxContentLength, theme); - } - - void PrintHookLine(std::ostream& stream, const cucumber::messages::TestStepFinished& testStepFinished, const cucumber::messages::Hook& hook, std::size_t scenarioIndent, std::size_t maxContentLength, bool isBeforeHook, bool useStatusIcon, const Theme& theme) // NOSONAR: cohesive print helper - { - PrintGherkinLine(stream, - helper::FormatHookTitle(hook, testStepFinished.testStepResult->status, isBeforeHook, useStatusIcon, theme), - helper::FormatCodeLocation(*hook.sourceReference, theme), - scenarioIndent + 2, maxContentLength, theme); - } - - void PrintStepLine(std::ostream& stream, const cucumber::messages::TestStepFinished& testStepFinished, const cucumber::messages::TestStep& testStep, const cucumber::messages::PickleStep& pickleStep, const cucumber::messages::Step& step, const cucumber::messages::StepDefinition* stepDefinition, std::size_t scenarioIndent, std::size_t maxContentLength, bool useStatusIcon, const Theme& theme) // NOSONAR: cohesive print helper - { - PrintGherkinLine(stream, - helper::FormatStepTitle(testStep, pickleStep, step, testStepFinished.testStepResult->status, useStatusIcon, theme), - helper::FormatCodeLocation(stepDefinition, theme), - scenarioIndent + 2, maxContentLength, theme); - } - - void PrintStepArgument(std::ostream& stream, const cucumber::messages::PickleStep& pickleStep, std::size_t scenarioIndent, bool useStatusIcon, const helper::Theme& theme) - { - const auto content = FormatPickleStepArgument(pickleStep, theme); - if (content.empty()) - return; - - PrintlnIndentedContent(stream, content, scenarioIndent + gherkinIndentLength + stepArgumentIndentLength + (useStatusIcon ? gherkinIndentLength : 0)); - } - - void PrintAmbiguousStep(std::ostream& stream, const cucumber::query::Query& query, const cucumber::messages::TestStepFinished& testStepFinished, const std::shared_ptr& testStep, std::size_t scenarioIndent, bool useStatusIcon, const Theme& theme) - { - if (testStepFinished.testStepResult->status != cucumber::messages::TestStepResultStatus::AMBIGUOUS) - return; - - const auto stepDefinitions = query.FindStepDefinitionsBy(testStep); - std::list list; - for (const auto& stepDefinition : stepDefinitions) - list.push_back(stepDefinition.get()); - const auto content = FormatAmbiguousStep(list, theme); - - if (content.empty()) - return; - - PrintlnIndentedContent(stream, content, scenarioIndent + gherkinIndentLength + errorIndentLength + (useStatusIcon ? gherkinIndentLength : 0)); - } - - void PrintError(std::ostream& stream, const cucumber::messages::TestStepFinished& testStepFinished, std::size_t scenarioIndent, bool useStatusIcon, const Theme& theme) - { - const auto content = FormatTestStepResultError(*testStepFinished.testStepResult, theme); - if (content.empty()) - return; - - PrintlnIndentedContent(stream, content, scenarioIndent + gherkinIndentLength + errorIndentLength + (useStatusIcon ? gherkinIndentLength : 0)); - } - - void PrintGherkinLine(std::ostream& stream, const std::string& title, const std::optional& location, std::size_t indent, std::size_t maxContentLength, const Theme& theme) - { - const auto unstyledLength = helper::Unstyled(title).length(); - const auto padding = location.has_value() ? (maxContentLength - std::min(unstyledLength, maxContentLength)) + 1 : 0; - - const std::string paddingStr(padding, ' '); - const auto content = fmt::format("{}{}{}", title, paddingStr, location.value_or("")); - - PrintlnIndentedContent(stream, content, indent); - } - - void PrintAttachment(std::ostream& stream, const cucumber::messages::Attachment& attachment, std::size_t scenarioIndent, bool useStatusIcon, const Theme& theme) - { - const auto content = FormatAttachment(attachment, theme); - const std::string indentStr(scenarioIndent + helper::gherkinIndentLength + helper::attachmentIndentLength + (useStatusIcon ? helper::gherkinIndentLength : 0), ' '); - - fmt::println(stream, "\n{}{}\n", indentStr, content); - } -} diff --git a/cucumber_cpp/library/formatter/helper/PrintMessages.hpp b/cucumber_cpp/library/formatter/helper/PrintMessages.hpp deleted file mode 100644 index 28687b22..00000000 --- a/cucumber_cpp/library/formatter/helper/PrintMessages.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef HELPER_PRINT_MESSAGES_HPP -#define HELPER_PRINT_MESSAGES_HPP - -#include "cucumber/messages/Attachment.hpp" -#include "cucumber/messages/Feature.hpp" -#include "cucumber/messages/Hook.hpp" -#include "cucumber/messages/Pickle.hpp" -#include "cucumber/messages/PickleStep.hpp" -#include "cucumber/messages/Rule.hpp" -#include "cucumber/messages/Scenario.hpp" -#include "cucumber/messages/Step.hpp" -#include "cucumber/messages/StepDefinition.hpp" -#include "cucumber/messages/TestStep.hpp" -#include "cucumber/messages/TestStepFinished.hpp" -#include "cucumber/query/Query.hpp" -#include "cucumber_cpp/library/formatter/helper/Theme.hpp" -#include -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - - static constexpr auto gherkinIndentLength = 2; - static constexpr auto stepArgumentIndentLength = 2; - static constexpr auto attachmentIndentLength = 4; - static constexpr auto errorIndentLength = 4; - - void PrintFeatureLine(std::ostream& stream, const cucumber::messages::Feature& feature, const Theme& theme); - void PrintRuleLine(std::ostream& stream, const cucumber::messages::Rule& rule, const Theme& theme); - void PrintTags(std::ostream& stream, const cucumber::messages::Pickle& pickle, std::size_t scenarioIndent, const Theme& theme); - void PrintScenarioLine(std::ostream& stream, const cucumber::messages::Pickle& pickle, const cucumber::messages::Scenario& scenario, std::size_t scenarioIndent, std::size_t maxContentLength, const Theme& theme); - void PrintScenarioAttemptLine(std::ostream& stream, const cucumber::messages::Pickle& pickle, std::size_t attempt, bool retry, const cucumber::messages::Scenario& scenario, std::size_t scenarioIndent, std::size_t maxContentLength, const Theme& theme); - void PrintHookLine(std::ostream& stream, const cucumber::messages::TestStepFinished& testStepFinished, const cucumber::messages::Hook& hook, std::size_t scenarioIndent, std::size_t maxContentLength, bool isBeforeHook, bool useStatusIcon, const Theme& theme); - void PrintStepLine(std::ostream& stream, const cucumber::messages::TestStepFinished& testStepFinished, const cucumber::messages::TestStep& testStep, const cucumber::messages::PickleStep& pickleStep, const cucumber::messages::Step& step, const cucumber::messages::StepDefinition* stepDefinition, std::size_t scenarioIndent, std::size_t maxContentLength, bool useStatusIcon, const Theme& theme); - void PrintStepArgument(std::ostream& stream, const cucumber::messages::PickleStep& pickleStep, std::size_t scenarioIndent, bool useStatusIcon, const helper::Theme& theme); - void PrintAmbiguousStep(std::ostream& stream, const cucumber::query::Query& query, const cucumber::messages::TestStepFinished& testStepFinished, const std::shared_ptr& testStep, std::size_t scenarioIndent, bool useStatusIcon, const Theme& theme); - void PrintError(std::ostream& stream, const cucumber::messages::TestStepFinished& testStepFinished, std::size_t scenarioIndent, bool useStatusIcon, const Theme& theme); - void PrintGherkinLine(std::ostream& stream, const std::string& title, const std::optional& location, std::size_t indent, std::size_t maxContentLength, const Theme& theme); - void PrintAttachment(std::ostream& stream, const cucumber::messages::Attachment& attachment, std::size_t scenarioIndent, bool useStatusIcon, const Theme& theme); -} - -#endif diff --git a/cucumber_cpp/library/formatter/helper/TextBuilder.cpp b/cucumber_cpp/library/formatter/helper/TextBuilder.cpp deleted file mode 100644 index 1ea51551..00000000 --- a/cucumber_cpp/library/formatter/helper/TextBuilder.cpp +++ /dev/null @@ -1,58 +0,0 @@ - -#include "cucumber_cpp/library/formatter/helper/TextBuilder.hpp" -#include "fmt/color.h" -#include "fmt/format.h" -#include "fmt/ranges.h" -#include -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - namespace - { - std::string ApplyStyle(std::string_view text, std::optional style) - { - if (!style) - return std::string{ text }; - - return fmt::format(*style, "{}", text); - } - } - - TextBuilder& TextBuilder::Space() - { - text += ' '; - return *this; - } - - TextBuilder& TextBuilder::Line() - { - text += '\n'; - return *this; - } - - TextBuilder& TextBuilder::Append(std::string_view text, std::optional style) - { - this->text += ApplyStyle(text, style); - return *this; - } - - std::string TextBuilder::Build(std::optional style, bool styleEachLine) const - { - if (styleEachLine) - { - auto styled = text | - std::views::split('\n') | - std::views::transform([&style](const auto& line) - { - return ApplyStyle(std::string{ line.begin(), line.end() }, style); - }); - - return fmt::to_string(fmt::join(styled, "\n")); - } - - return ApplyStyle(text, style); - } -} diff --git a/cucumber_cpp/library/formatter/helper/TextBuilder.hpp b/cucumber_cpp/library/formatter/helper/TextBuilder.hpp deleted file mode 100644 index fd746f8c..00000000 --- a/cucumber_cpp/library/formatter/helper/TextBuilder.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef HELPER_TEXT_BUILDER_HPP -#define HELPER_TEXT_BUILDER_HPP - -#include "fmt/color.h" -#include -#include -#include - -namespace cucumber_cpp::library::formatter::helper -{ - struct TextBuilder - { - TextBuilder& Space(); - TextBuilder& Line(); - TextBuilder& Append(std::string_view text, std::optional style = std::nullopt); - std::string Build(std::optional style = std::nullopt, bool styleEachLine = false) const; - - private: - std::string text; - }; -} - -#endif diff --git a/cucumber_cpp/library/plugin/CMakeLists.txt b/cucumber_cpp/library/plugin/CMakeLists.txt index a60992b0..3db935e8 100644 --- a/cucumber_cpp/library/plugin/CMakeLists.txt +++ b/cucumber_cpp/library/plugin/CMakeLists.txt @@ -20,7 +20,7 @@ target_include_directories(cucumber_cpp.library.plugin PUBLIC target_link_libraries(cucumber_cpp.library.plugin PUBLIC cucumber_cpp.library.support - cucumber_cpp.library.cucumber_expression + cucumber::cucumber_expressions ${CMAKE_DL_LIBS} ) diff --git a/cucumber_cpp/library/plugin/DynamicLibraryManager.cpp b/cucumber_cpp/library/plugin/DynamicLibraryManager.cpp index 24940f6b..784d6d2a 100644 --- a/cucumber_cpp/library/plugin/DynamicLibraryManager.cpp +++ b/cucumber_cpp/library/plugin/DynamicLibraryManager.cpp @@ -1,5 +1,5 @@ #include "cucumber_cpp/library/plugin/DynamicLibraryManager.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/plugin/DynamicLibrary.hpp" #include "cucumber_cpp/library/plugin/PluginExport.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" @@ -66,7 +66,7 @@ namespace cucumber_cpp::library::plugin .registration = &support::DefinitionRegistration::Instance(), .stopwatch = &util::Stopwatch::Instance(), .timestampGenerator = &util::TimestampGenerator::Instance(), - .converterMap = &cucumber_expression::ConverterRegistry::Instance(), + .converterMap = &cucumber::cucumber_expressions::ConverterRegistry::Instance(), }; registerFn(&context); diff --git a/cucumber_cpp/library/plugin/PLUGIN_ARCHITECTURE.md b/cucumber_cpp/library/plugin/PLUGIN_ARCHITECTURE.md index 85159084..e99cecc8 100644 --- a/cucumber_cpp/library/plugin/PLUGIN_ARCHITECTURE.md +++ b/cucumber_cpp/library/plugin/PLUGIN_ARCHITECTURE.md @@ -385,7 +385,7 @@ Before loading plugins, the host takes snapshots of mutable global state: ```cpp support::DefinitionRegistration::Instance().TakeSnapshot(); -cucumber_expression::ConverterRegistry::TakeSnapshot(); +cucumber::cucumber_expressions::ConverterRegistry::TakeSnapshot(); dynamicLibraryManager.Load(options.loadPaths); ``` @@ -394,7 +394,7 @@ statically-registered converters), the host restores to the snapshot: ```cpp support::DefinitionRegistration::Instance().UnregisterPlugins(); -cucumber_expression::ConverterRegistry::RestoreSnapshot(); +cucumber::cucumber_expressions::ConverterRegistry::RestoreSnapshot(); dynamicLibraryManager.UnloadAll(); ``` diff --git a/cucumber_cpp/library/plugin/ParameterLoader.cpp b/cucumber_cpp/library/plugin/ParameterLoader.cpp index 232848d3..ec383c7b 100644 --- a/cucumber_cpp/library/plugin/ParameterLoader.cpp +++ b/cucumber_cpp/library/plugin/ParameterLoader.cpp @@ -1,11 +1,11 @@ #include "cucumber_cpp/library/plugin/ParameterLoader.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" #include namespace cucumber_cpp::library::plugin { - void ParameterLoader::Load(const support::DefinitionRegistration& registration, cucumber_expression::ParameterRegistry& parameterRegistry) + void ParameterLoader::Load(const support::DefinitionRegistration& registration, cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry) { const auto& existingParameters = parameterRegistry.GetParameters(); @@ -14,7 +14,7 @@ namespace cucumber_cpp::library::plugin if (!existingParameters.contains(parameter.params.name)) { parameterRegistry.AddParameter( - cucumber_expression::ParameterType{ + cucumber::cucumber_expressions::ParameterType{ .name = parameter.params.name, .regex = { std::string(parameter.params.regex) }, .isBuiltin = false, diff --git a/cucumber_cpp/library/plugin/ParameterLoader.hpp b/cucumber_cpp/library/plugin/ParameterLoader.hpp index 720d6e2f..c3d06165 100644 --- a/cucumber_cpp/library/plugin/ParameterLoader.hpp +++ b/cucumber_cpp/library/plugin/ParameterLoader.hpp @@ -1,14 +1,14 @@ #ifndef PLUGIN_PARAMETER_LOADER_HPP #define PLUGIN_PARAMETER_LOADER_HPP -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" namespace cucumber_cpp::library::plugin { struct ParameterLoader { - static void Load(const support::DefinitionRegistration& registration, cucumber_expression::ParameterRegistry& parameterRegistry); + static void Load(const support::DefinitionRegistration& registration, cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry); }; } diff --git a/cucumber_cpp/library/plugin/PluginRegister.cpp b/cucumber_cpp/library/plugin/PluginRegister.cpp index 5ea1e5fb..123054d1 100644 --- a/cucumber_cpp/library/plugin/PluginRegister.cpp +++ b/cucumber_cpp/library/plugin/PluginRegister.cpp @@ -1,4 +1,4 @@ -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/plugin/PluginExport.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" #include "cucumber_cpp/library/util/Duration.hpp" @@ -30,8 +30,8 @@ extern "C" CCR_EXPORT void ccr_register(cucumber_cpp::library::plugin::PluginHos if (context->converterMap != nullptr) { - auto* hostMap = static_cast(context->converterMap); - auto& local = cucumber_cpp::library::cucumber_expression::ConverterRegistry::LocalInstance(); + auto* hostMap = static_cast(context->converterMap); + auto& local = cucumber::cucumber_expressions::ConverterRegistry::LocalInstance(); // On Linux (ELF unique symbols), host and plugin share the same // static map, so migration is unnecessary and clearing would @@ -48,7 +48,7 @@ extern "C" CCR_EXPORT void ccr_register(cucumber_cpp::library::plugin::PluginHos // Redirect all future registrations and lookups in this plugin to the // host-owned converter map so host and plugin share the same storage. - cucumber_cpp::library::cucumber_expression::ConverterRegistry::SetInstance(hostMap); + cucumber::cucumber_expressions::ConverterRegistry::SetInstance(hostMap); } } } diff --git a/cucumber_cpp/library/runtime/CMakeLists.txt b/cucumber_cpp/library/runtime/CMakeLists.txt index ce295e84..8f9f66f8 100644 --- a/cucumber_cpp/library/runtime/CMakeLists.txt +++ b/cucumber_cpp/library/runtime/CMakeLists.txt @@ -21,7 +21,7 @@ target_include_directories(cucumber_cpp.library.runtime PUBLIC target_link_libraries(cucumber_cpp.library.runtime PUBLIC cucumber_cpp.library.assemble - cucumber_cpp.library.cucumber_expression + cucumber::cucumber_expressions cucumber_cpp.library.support cucumber_cpp.library.util fmt::fmt diff --git a/cucumber_cpp/library/runtime/Coordinator.cpp b/cucumber_cpp/library/runtime/Coordinator.cpp index ba103403..d383af72 100644 --- a/cucumber_cpp/library/runtime/Coordinator.cpp +++ b/cucumber_cpp/library/runtime/Coordinator.cpp @@ -28,18 +28,24 @@ namespace cucumber_cpp::library::runtime bool Coordinator::Run() { - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::TestRunStarted{ - .timestamp = util::MakeShared(util::TimestampNow()), - .id = std::string{ testRunStartedId }, - })); + broadcaster.BroadcastEvent([this](cucumber::messages::Envelope& envelope) + { + cucumber::messages::TestRunStarted testRunStarted; + testRunStarted.timestamp = util::TimestampNow(); + testRunStarted.id = std::string{ testRunStartedId }; + envelope.testRunStarted = std::move(testRunStarted); + }); const auto success = runtimeAdapter->Run(); - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::TestRunFinished{ - .success = success, - .timestamp = util::MakeShared(util::TimestampNow()), - .testRunStartedId = std::string{ testRunStartedId }, - })); + broadcaster.BroadcastEvent([this, &success](cucumber::messages::Envelope& envelope) + { + cucumber::messages::TestRunFinished testRunFinished; + testRunFinished.success = success; + testRunFinished.timestamp = util::TimestampNow(); + testRunFinished.testRunStartedId = std::string{ testRunStartedId }; + envelope.testRunFinished = std::move(testRunFinished); + }); return success; } diff --git a/cucumber_cpp/library/runtime/NestedTestCaseRunner.cpp b/cucumber_cpp/library/runtime/NestedTestCaseRunner.cpp index 327c4aa1..7218a872 100644 --- a/cucumber_cpp/library/runtime/NestedTestCaseRunner.cpp +++ b/cucumber_cpp/library/runtime/NestedTestCaseRunner.cpp @@ -1,18 +1,13 @@ #include "cucumber_cpp/library/runtime/NestedTestCaseRunner.hpp" -#include "cucumber/messages/Duration.hpp" -#include "cucumber/messages/Group.hpp" #include "cucumber/messages/PickleDocString.hpp" #include "cucumber/messages/PickleTable.hpp" -#include "cucumber/messages/StepMatchArgument.hpp" #include "cucumber/messages/StepMatchArgumentsList.hpp" #include "cucumber/messages/TestStep.hpp" #include "cucumber/messages/TestStepResultStatus.hpp" #include "cucumber_cpp/library/Context.hpp" -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Matcher.hpp" +#include "cucumber_cpp/library/support/MatchStepDefinitions.hpp" #include "cucumber_cpp/library/support/StepRegistry.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" -#include "cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp" #include "cucumber_cpp/library/util/Body.hpp" #include "cucumber_cpp/library/util/Broadcaster.hpp" #include "cucumber_cpp/library/util/NestedTestCaseRunnerError.hpp" @@ -24,54 +19,24 @@ #include "cucumber_cpp/library/util/TransformTable.hpp" #include "cucumber_cpp/library/util/TransformTestStepResult.hpp" #include -#include +#include #include #include #include -#include -#include #include namespace cucumber_cpp::library::runtime { namespace { - auto TransformToMatch(const std::string& text) - { - return [&text](const support::StepRegistry::Definition& definition) -> std::pair>> - { - const auto match = std::visit(cucumber_expression::MatchVisitor{ text }, definition.regex); - return { definition.id, match }; - }; - } - - bool HasMatch(const std::pair>>& pair) - { - return pair.second.has_value(); - } - cucumber::messages::TestStep Assemble(const std::string& step, const support::SupportCodeLibrary& supportCodeLibrary, const util::TestStepStarted& testStepStarted) { - cucumber::messages::TestStep testStep{ - .id = testStepStarted.testStepId, - .stepDefinitionIds = std::vector{}, - .stepMatchArgumentsLists = std::vector>{} - }; + cucumber::messages::TestStep testStep; + testStep.id = testStepStarted.testStepId; + testStep.stepDefinitionIds = std::vector{}; + testStep.stepMatchArgumentsLists = std::vector{}; - const auto& stepDefinitions = supportCodeLibrary.stepRegistry.StepDefinitions(); - - for (const auto& [id, match] : stepDefinitions | - std::views::transform(TransformToMatch(step)) | - std::views::filter(HasMatch)) - { - testStep.stepDefinitionIds.value().push_back(id); - const auto& argumentList = testStep.stepMatchArgumentsLists.value().emplace_back(std::make_shared()); - for (const auto& result : *match) - argumentList->stepMatchArguments.emplace_back(std::make_shared(cucumber::messages::StepMatchArgument{ - .group = std::make_shared(util::ArgumentGroupToMessageGroup(result.Group())), - .parameterTypeName = result.Name().empty() ? std::nullopt : std::make_optional(result.Name()), - })); - } + support::MatchStepDefinitions(testStep, supportCodeLibrary.stepRegistry.StepDefinitions(), step); return testStep; } @@ -90,25 +55,24 @@ namespace cucumber_cpp::library::runtime void Run(std::size_t nesting, const std::string& step, const cucumber::messages::TestStep& testStep, const support::SupportCodeLibrary& supportCodeLibrary, util::Broadcaster& broadcaster, Context& testCaseContext, const util::TestStepStarted& testStepStarted, const std::optional& dataTable, const std::optional& docString) // NOSONAR: cohesive step runner { - auto stepDefinitions = (*testStep.stepDefinitionIds) | std::views::transform([&supportCodeLibrary](const std::string& id) + auto stepDefinitions = (*testStep.stepDefinitionIds) | std::views::transform([&supportCodeLibrary](const std::string& id) -> const support::StepRegistry::Definition& { return supportCodeLibrary.stepRegistry.GetDefinitionById(id); }); if (testStep.stepDefinitionIds->empty()) - throw util::NestedTestCaseRunnerError{ .nesting = nesting, .status = { - .duration = std::make_shared(), - .status = cucumber::messages::TestStepResultStatus::UNDEFINED, - }, - .text = step }; - + { + cucumber::messages::TestStepResult status{}; + status.status = cucumber::messages::TestStepResultStatus::UNDEFINED; + throw util::NestedTestCaseRunnerError{ .nesting = nesting, .status = status, .text = step }; + } else if (testStep.stepDefinitionIds->size() > 1) - throw util::NestedTestCaseRunnerError{ .nesting = nesting, .status = { - .duration = std::make_shared(), - .message = "Ambiguous step definitions", - .status = cucumber::messages::TestStepResultStatus::AMBIGUOUS, - }, - .text = step }; + { + cucumber::messages::TestStepResult status{}; + status.message = "Ambiguous step definitions"; + status.status = cucumber::messages::TestStepResultStatus::AMBIGUOUS; + throw util::NestedTestCaseRunnerError{ .nesting = nesting, .status = status, .text = step }; + } else { const auto& definition = stepDefinitions.front(); @@ -117,7 +81,7 @@ namespace cucumber_cpp::library::runtime { return definition.factory(testStepResult, nestedTestCaseRunner, broadcaster, testCaseContext, testStepStarted, util::TransformTable(dataTable), util::TransformDocString(docString)); }; - Invoke(nesting, step, bodyFactory, *testStep.stepMatchArgumentsLists->front()); + Invoke(nesting, step, bodyFactory, testStep.stepMatchArgumentsLists->front()); } } } diff --git a/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp b/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp index 6142e68d..256a5c81 100644 --- a/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp +++ b/cucumber_cpp/library/runtime/SerialRuntimeAdapter.cpp @@ -53,7 +53,7 @@ namespace cucumber_cpp::library::runtime if (!failing) { - auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, idGenerator); + auto assembledTestSuites = assemble::AssembleTestSuites(supportCodeLibrary, testRunStartedId, broadcaster, sourcedPickles, *idGenerator); for (const auto& assembledTestSuite : assembledTestSuites) { diff --git a/cucumber_cpp/library/runtime/TestCaseRunner.cpp b/cucumber_cpp/library/runtime/TestCaseRunner.cpp index 5074e481..a00598f4 100644 --- a/cucumber_cpp/library/runtime/TestCaseRunner.cpp +++ b/cucumber_cpp/library/runtime/TestCaseRunner.cpp @@ -27,7 +27,6 @@ #include "cucumber_cpp/library/util/Duration.hpp" #include "cucumber_cpp/library/util/GetWorstTestStepResult.hpp" #include "cucumber_cpp/library/util/HookData.hpp" -#include "cucumber_cpp/library/util/MakeShared.hpp" #include "cucumber_cpp/library/util/ScenarioInfo.hpp" #include "cucumber_cpp/library/util/TestStepResult.hpp" #include "cucumber_cpp/library/util/Timestamp.hpp" @@ -39,7 +38,6 @@ #include "cucumber_cpp/library/util/TransformTestStepStarted.hpp" #include #include -#include #include #include #include @@ -103,38 +101,43 @@ namespace cucumber_cpp::library::runtime const auto currentTestCaseStartedId = idGenerator->NextId(); bool willRetry = false; - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::TestCaseStarted{ - .attempt = attempt, - .id = currentTestCaseStartedId, - .testCaseId = testCase.id, - .timestamp = util::MakeShared(util::TimestampNow()), - })); + broadcaster.BroadcastEvent([this, attempt, ¤tTestCaseStartedId](cucumber::messages::Envelope& envelope) + { + cucumber::messages::TestCaseStarted testCaseStarted; + testCaseStarted.attempt = attempt; + testCaseStarted.id = currentTestCaseStartedId; + testCaseStarted.testCaseId = testCase.id; + testCaseStarted.timestamp = util::TimestampNow(); + envelope.testCaseStarted = std::move(testCaseStarted); + }); bool seenSteps = false; bool error = false; for (const auto& testStep : testCase.testSteps) { - auto testStepStarted = cucumber::messages::TestStepStarted{ - .testCaseStartedId = currentTestCaseStartedId, - .testStepId = testStep->id, - .timestamp = std::make_shared(util::TimestampNow()), - }; - broadcaster.BroadcastEvent(util::MakeShared(testStepStarted)); + cucumber::messages::TestStepStarted testStepStarted; + testStepStarted.testCaseStartedId = currentTestCaseStartedId; + testStepStarted.testStepId = testStep.id; + testStepStarted.timestamp = util::TimestampNow(); + broadcaster.BroadcastEvent([&testStepStarted](cucumber::messages::Envelope& envelope) + { + envelope.testStepStarted = testStepStarted; + }); cucumber::messages::TestStepResult testStepResult; - if (testStep->hookId) + if (testStep.hookId) { - testStepResult = RunHook(supportCodeLibrary.hookRegistry.GetDefinitionById(testStep->hookId.value()), !seenSteps, testCaseContext, testStepStarted, error); + testStepResult = RunHook(supportCodeLibrary.hookRegistry.GetDefinitionById(testStep.hookId.value()), !seenSteps, testCaseContext, testStepStarted, error); } else { auto pickleStepIter = std::ranges::find_if(pickle.steps, [&](const auto& pickleStep) { - return pickleStep->id == testStep->pickleStepId.value(); + return pickleStep.id == testStep.pickleStepId.value(); }); - testStepResult = RunStep(**pickleStepIter, *testStep, testCaseContext, testStepStarted); + testStepResult = RunStep(*pickleStepIter, testStep, testCaseContext, testStepStarted); seenSteps = true; if (testStepResult.message.has_value() || testStepResult.exception.has_value()) @@ -142,21 +145,27 @@ namespace cucumber_cpp::library::runtime } testStepResults.emplace_back(testStepResult); - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::TestStepFinished{ - .testCaseStartedId = currentTestCaseStartedId, - .testStepId = testStep->id, - .testStepResult = util::MakeShared(testStepResult), - .timestamp = util::MakeShared(util::TimestampNow()), - })); + broadcaster.BroadcastEvent([¤tTestCaseStartedId, &testStep, &testStepResult](cucumber::messages::Envelope& envelope) + { + cucumber::messages::TestStepFinished testStepFinished; + testStepFinished.testCaseStartedId = currentTestCaseStartedId; + testStepFinished.testStepId = testStep.id; + testStepFinished.testStepResult = testStepResult; + testStepFinished.timestamp = util::TimestampNow(); + envelope.testStepFinished = std::move(testStepFinished); + }); } willRetry = GetWorstStepResult().status == cucumber::messages::TestStepResultStatus::FAILED && moreAttemptsAvailable; - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::TestCaseFinished{ - .testCaseStartedId = currentTestCaseStartedId, - .timestamp = util::MakeShared(util::TimestampNow()), - .willBeRetried = willRetry, - })); + broadcaster.BroadcastEvent([¤tTestCaseStartedId, &willRetry](cucumber::messages::Envelope& envelope) + { + cucumber::messages::TestCaseFinished testCaseFinished; + testCaseFinished.testCaseStartedId = currentTestCaseStartedId; + testCaseFinished.timestamp = util::TimestampNow(); + testCaseFinished.willBeRetried = willRetry; + envelope.testCaseFinished = std::move(testCaseFinished); + }); return willRetry; } @@ -164,10 +173,11 @@ namespace cucumber_cpp::library::runtime cucumber::messages::TestStepResult TestCaseRunner::RunHook(const support::HookRegistry::Definition& hookDefinition, bool isBeforeHook, Context& testCaseContext, const cucumber::messages::TestStepStarted& testStepStarted, bool hasError) { if (ShouldSkipHook(isBeforeHook)) - return { - .duration = std::make_shared(), - .status = cucumber::messages::TestStepResultStatus::SKIPPED, - }; + { + cucumber::messages::TestStepResult testStepResult{}; + testStepResult.status = cucumber::messages::TestStepResultStatus::SKIPPED; + return testStepResult; + } const util::BodyFactory bodyFactory = [&hookDefinition, this, &testCaseContext, &testStepStarted, hasError](util::TestStepResult& testStepResult) { @@ -199,46 +209,45 @@ namespace cucumber_cpp::library::runtime cucumber::messages::TestStepResult TestCaseRunner::RunStep(const cucumber::messages::PickleStep& pickleStep, const cucumber::messages::TestStep& testStep, Context& testCaseContext, const cucumber::messages::TestStepStarted& testStepStarted) { - auto stepDefinitions = (*testStep.stepDefinitionIds) | std::views::transform([this](const std::string& id) + auto stepDefinitions = (*testStep.stepDefinitionIds) | std::views::transform([this](const std::string& id) -> const support::StepRegistry::Definition& { return supportCodeLibrary.stepRegistry.GetDefinitionById(id); }); if (const auto count = testStep.stepDefinitionIds->size(); count == 0) { - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::Suggestion{ - .id = idGenerator->NextId(), - .pickleStepId = pickleStep.id, - .snippets = {}, - })); - - return { - .duration = std::make_shared(), - .status = cucumber::messages::TestStepResultStatus::UNDEFINED, - }; + broadcaster.BroadcastEvent([this, &pickleStep](cucumber::messages::Envelope& envelope) + { + cucumber::messages::Suggestion suggestion; + suggestion.id = idGenerator->NextId(); + suggestion.pickleStepId = pickleStep.id; + envelope.suggestion = std::move(suggestion); + }); + + cucumber::messages::TestStepResult testStepResult{}; + testStepResult.status = cucumber::messages::TestStepResultStatus::UNDEFINED; + return testStepResult; } else if (count > 1) { - return { - .duration = std::make_shared(), - .message = "Ambiguous step definitions", - .status = cucumber::messages::TestStepResultStatus::AMBIGUOUS, - }; + cucumber::messages::TestStepResult testStepResult{}; + testStepResult.message = "Ambiguous step definitions"; + testStepResult.status = cucumber::messages::TestStepResultStatus::AMBIGUOUS; + return testStepResult; } else if (IsSkippingSteps()) { - return { - .duration = std::make_shared(), - .status = cucumber::messages::TestStepResultStatus::SKIPPED, - }; + cucumber::messages::TestStepResult testStepResult{}; + testStepResult.status = cucumber::messages::TestStepResultStatus::SKIPPED; + return testStepResult; } auto stepResults = RunStepHooks(pickleStep, util::HookType::beforeStep, testCaseContext, testStepStarted); if (util::GetWorstTestStepResult(stepResults).status != cucumber::messages::TestStepResultStatus::FAILED) { - const auto dataTable = (pickleStep.argument && (*pickleStep.argument)->dataTable) ? std::make_optional(*(*(*pickleStep.argument)->dataTable)) : std::nullopt; - const auto docString = (pickleStep.argument && (*pickleStep.argument)->docString) ? std::make_optional(*(*(*pickleStep.argument)->docString)) : std::nullopt; + const auto dataTable = (pickleStep.argument && pickleStep.argument->dataTable) ? std::make_optional(*pickleStep.argument->dataTable) : std::nullopt; + const auto docString = (pickleStep.argument && pickleStep.argument->docString) ? std::make_optional(*pickleStep.argument->docString) : std::nullopt; const auto& definition = stepDefinitions.front(); @@ -249,7 +258,7 @@ namespace cucumber_cpp::library::runtime return definition.factory(testStepResult, nestedTestCaseRunner, broadcaster, testCaseContext, util::TransformTestStepStarted(testStepStarted), util::TransformTable(dataTable), util::TransformDocString(docString)); }; - stepResults.push_back(InvokeStep(bodyFactory, *testStep.stepMatchArgumentsLists->front())); + stepResults.push_back(InvokeStep(bodyFactory, testStep.stepMatchArgumentsLists->front())); } const auto afterStepHookResults = RunStepHooks(pickleStep, util::HookType::afterStep, testCaseContext, testStepStarted); @@ -260,19 +269,20 @@ namespace cucumber_cpp::library::runtime cucumber::messages::Duration finalDuration{}; for (const auto& stepResult : stepResults) - if (stepResult.duration) - finalDuration += *stepResult.duration; + finalDuration += stepResult.duration; - finalStepResult.duration = std::make_shared(finalDuration); + finalStepResult.duration = finalDuration; return finalStepResult; } cucumber::messages::TestStepResult TestCaseRunner::GetWorstStepResult() const { if (testStepResults.empty()) - return { - .status = skip ? cucumber::messages::TestStepResultStatus::SKIPPED : cucumber::messages::TestStepResultStatus::PASSED, - }; + { + cucumber::messages::TestStepResult testStepResult{}; + testStepResult.status = skip ? cucumber::messages::TestStepResultStatus::SKIPPED : cucumber::messages::TestStepResultStatus::PASSED; + return testStepResult; + } return util::GetWorstTestStepResult(testStepResults); } diff --git a/cucumber_cpp/library/runtime/Worker.cpp b/cucumber_cpp/library/runtime/Worker.cpp index 56562e8d..7b8a98c2 100644 --- a/cucumber_cpp/library/runtime/Worker.cpp +++ b/cucumber_cpp/library/runtime/Worker.cpp @@ -114,7 +114,7 @@ namespace cucumber_cpp::library::runtime if (options.featureHooks) { - const auto beforeHookResults = RunBeforeTestSuiteHooks(**assembledTestSuite.gherkinDocument.feature, testSuiteContext); + const auto beforeHookResults = RunBeforeTestSuiteHooks(*assembledTestSuite.gherkinDocument.feature, testSuiteContext); if (IsFailing(util::GetWorstTestStepResult(beforeHookResults).status, options.dryRun)) failing = true; @@ -125,7 +125,7 @@ namespace cucumber_cpp::library::runtime if (options.featureHooks) { - const auto afterHookResults = RunAfterTestSuiteHooks(**assembledTestSuite.gherkinDocument.feature, testSuiteContext); + const auto afterHookResults = RunAfterTestSuiteHooks(*assembledTestSuite.gherkinDocument.feature, testSuiteContext); if (IsFailing(util::GetWorstTestStepResult(afterHookResults).status, options.dryRun)) failing = true; @@ -182,16 +182,19 @@ namespace cucumber_cpp::library::runtime const auto& definition = supportCodeLibrary.hookRegistry.GetDefinitionById(id); const auto testRunHookStartedId = idGenerator->NextId(); - const auto testRunHookStarted = cucumber::messages::TestRunHookStarted{ - .id = testRunHookStartedId, - .testRunStartedId = std::string{ testRunStartedId }, - .hookId = definition.data.id, - .timestamp = std::make_shared(util::TimestampNow()), - }; + cucumber::messages::TestRunHookStarted testRunHookStarted; + testRunHookStarted.id = testRunHookStartedId; + testRunHookStarted.testRunStartedId = std::string{ testRunStartedId }; + testRunHookStarted.hookId = definition.data.id; + testRunHookStarted.timestamp = util::TimestampNow(); - broadcaster.BroadcastEvent(util::MakeShared(testRunHookStarted)); + broadcaster.BroadcastEvent([&testRunHookStarted](cucumber::messages::Envelope& envelope) + { + envelope.testRunHookStarted = testRunHookStarted; + }); - cucumber::messages::TestStepResult result{ .duration = std::make_shared(cucumber::messages::Duration{ .seconds = 0, .nanos = 0 }), .status = cucumber::messages::TestStepResultStatus::SKIPPED }; + cucumber::messages::TestStepResult result{}; + result.status = cucumber::messages::TestStepResultStatus::SKIPPED; if (!options.dryRun) { @@ -206,11 +209,14 @@ namespace cucumber_cpp::library::runtime throw GlobalHookError{ fmt::format("Global Hook Failed: {}\nresult:{}", util::TransformHookData(definition.data).to_string(), result.to_string()) }; } - broadcaster.BroadcastEvent(util::MakeShared(cucumber::messages::TestRunHookFinished{ - .testRunHookStartedId = testRunHookStartedId, - .result = util::MakeShared(result), - .timestamp = util::MakeShared(util::TimestampNow()), - })); + broadcaster.BroadcastEvent([&testRunHookStartedId, &result](cucumber::messages::Envelope& envelope) + { + cucumber::messages::TestRunHookFinished testRunHookFinished; + testRunHookFinished.testRunHookStartedId = testRunHookStartedId; + testRunHookFinished.result = result; + testRunHookFinished.timestamp = util::TimestampNow(); + envelope.testRunHookFinished = std::move(testRunHookFinished); + }); return result; } diff --git a/cucumber_cpp/library/support/CMakeLists.txt b/cucumber_cpp/library/support/CMakeLists.txt index df8c9be6..ee9d2338 100644 --- a/cucumber_cpp/library/support/CMakeLists.txt +++ b/cucumber_cpp/library/support/CMakeLists.txt @@ -12,6 +12,8 @@ target_sources(cucumber_cpp.library.support PRIVATE DefinitionRegistration.hpp HookRegistry.cpp HookRegistry.hpp + MatchStepDefinitions.cpp + MatchStepDefinitions.hpp StepRegistry.cpp StepRegistry.hpp StepType.hpp @@ -27,7 +29,7 @@ target_include_directories(cucumber_cpp.library.support PUBLIC target_link_libraries(cucumber_cpp.library.support PUBLIC GTest::gtest GTest::gmock - cucumber_cpp.library.cucumber_expression + cucumber::cucumber_expressions cucumber::tag_expressions cucumber_cpp.library.util fmt::fmt diff --git a/cucumber_cpp/library/support/DefinitionRegistration.cpp b/cucumber_cpp/library/support/DefinitionRegistration.cpp index 1430b66f..7b80a23f 100644 --- a/cucumber_cpp/library/support/DefinitionRegistration.cpp +++ b/cucumber_cpp/library/support/DefinitionRegistration.cpp @@ -1,6 +1,6 @@ #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber/gherkin/IdGenerator.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/StepRegistry.hpp" #include "cucumber_cpp/library/support/StepType.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" @@ -98,7 +98,7 @@ namespace cucumber_cpp::library::support return result; } - std::set> DefinitionRegistration::GetRegisteredParameters() const + std::set> DefinitionRegistration::GetRegisteredParameters() const { auto result = staticCustomParameters; result.insert(customParameters.begin(), customParameters.end()); diff --git a/cucumber_cpp/library/support/DefinitionRegistration.hpp b/cucumber_cpp/library/support/DefinitionRegistration.hpp index 254788ad..b7948ced 100644 --- a/cucumber_cpp/library/support/DefinitionRegistration.hpp +++ b/cucumber_cpp/library/support/DefinitionRegistration.hpp @@ -1,8 +1,9 @@ #ifndef SUPPORT_DEFINITION_REGISTRATION_HPP #define SUPPORT_DEFINITION_REGISTRATION_HPP +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/SourceLocation.hpp" #include "cucumber/gherkin/IdGenerator.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/StepRegistry.hpp" #include "cucumber_cpp/library/support/StepType.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" @@ -41,7 +42,7 @@ namespace cucumber_cpp::library::support std::vector GetHooks(); - [[nodiscard]] std::set> GetRegisteredParameters() const; + [[nodiscard]] std::set> GetRegisteredParameters() const; template static std::size_t Register(Hook hook, util::HookType hookType, std::source_location sourceLocation = std::source_location::current()); @@ -53,7 +54,7 @@ namespace cucumber_cpp::library::support static std::size_t Register(std::string_view matcher, StepType stepType, std::source_location sourceLocation = std::source_location::current()); template - static std::size_t Register(cucumber_expression::CustomParameterEntryParams params, std::source_location location = std::source_location::current()); + static std::size_t Register(cucumber::cucumber_expressions::CustomParameterEntryParams params, cucumber::cucumber_expressions::SourceLocation location = cucumber::cucumber_expressions::SourceLocation::current()); private: std::size_t Register(Hook hook, util::HookType hookType, util::HookFactory factory, std::source_location sourceLocation); @@ -61,12 +62,12 @@ namespace cucumber_cpp::library::support std::size_t Register(std::string_view matcher, StepType stepType, util::StepFactory factory, std::source_location sourceLocation); std::map registry; - std::set> customParameters; + std::set> customParameters; std::vector plugins; std::map staticRegistry; - std::set> staticCustomParameters; + std::set> staticCustomParameters; }; ////////////////////////// @@ -119,12 +120,12 @@ namespace cucumber_cpp::library::support } template - std::size_t DefinitionRegistration::Register(cucumber_expression::CustomParameterEntryParams params, std::source_location location) + std::size_t DefinitionRegistration::Register(cucumber::cucumber_expressions::CustomParameterEntryParams params, cucumber::cucumber_expressions::SourceLocation location) { auto& instance = Instance(); instance.customParameters.emplace(params, instance.customParameters.size() + 1, location); - cucumber_expression::ConverterTypeMap::Instance()[params.name] = Transformer::Transform; + cucumber::cucumber_expressions::ConverterTypeMap::Instance()[params.name] = Transformer::Transform; return instance.customParameters.size(); } diff --git a/cucumber_cpp/library/support/MatchStepDefinitions.cpp b/cucumber_cpp/library/support/MatchStepDefinitions.cpp new file mode 100644 index 00000000..dca84745 --- /dev/null +++ b/cucumber_cpp/library/support/MatchStepDefinitions.cpp @@ -0,0 +1,52 @@ +#include "cucumber_cpp/library/support/MatchStepDefinitions.hpp" +#include "cucumber/cucumber-expressions/Argument.hpp" +#include "cucumber/cucumber-expressions/Matcher.hpp" +#include "cucumber/messages/Group.hpp" +#include "cucumber/messages/StepMatchArgument.hpp" +#include "cucumber/messages/StepMatchArgumentsList.hpp" +#include "cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp" +#include +#include +#include +#include +#include +#include + +namespace cucumber_cpp::library::support +{ + namespace + { + auto TransformToMatch(const std::string& text) + { + return [&text](const StepRegistry::Definition& definition) -> std::pair>> + { + const auto match = std::visit(cucumber::cucumber_expressions::MatchVisitor{ text }, definition.regex); + return { definition.id, match }; + }; + } + + bool HasMatch(const std::pair>>& pair) + { + return pair.second.has_value(); + } + } + + void MatchStepDefinitions(cucumber::messages::TestStep& testStep, const std::list& stepDefinitions, const std::string& text) + { + for (const auto& [id, match] : stepDefinitions | + std::views::transform(TransformToMatch(text)) | + std::views::filter(HasMatch)) + { + testStep.stepDefinitionIds.value().push_back(id); + auto& argumentList = testStep.stepMatchArgumentsLists.value().emplace_back(); + for (const auto& result : *match) + { + cucumber::messages::StepMatchArgument stepMatchArgument; + stepMatchArgument.group = util::ArgumentGroupToMessageGroup(result.Group()); + if (!result.Name().empty()) + stepMatchArgument.parameterTypeName = result.Name(); + argumentList.stepMatchArguments.push_back(stepMatchArgument); + } + } + } +} diff --git a/cucumber_cpp/library/support/MatchStepDefinitions.hpp b/cucumber_cpp/library/support/MatchStepDefinitions.hpp new file mode 100644 index 00000000..1b1adf79 --- /dev/null +++ b/cucumber_cpp/library/support/MatchStepDefinitions.hpp @@ -0,0 +1,15 @@ +#ifndef SUPPORT_MATCH_STEP_DEFINITIONS_HPP +#define SUPPORT_MATCH_STEP_DEFINITIONS_HPP + +#include "cucumber/messages/TestStep.hpp" +#include "cucumber_cpp/library/support/StepRegistry.hpp" +#include +#include + +namespace cucumber_cpp::library::support +{ + // Populates testStep.stepDefinitionIds/stepMatchArgumentsLists (must already be set) with every definition matching text. + void MatchStepDefinitions(cucumber::messages::TestStep& testStep, const std::list& stepDefinitions, const std::string& text); +} + +#endif diff --git a/cucumber_cpp/library/support/StepRegistry.cpp b/cucumber_cpp/library/support/StepRegistry.cpp index 0fc89c30..a2ffa4d0 100644 --- a/cucumber_cpp/library/support/StepRegistry.cpp +++ b/cucumber_cpp/library/support/StepRegistry.cpp @@ -1,11 +1,11 @@ #include "cucumber_cpp/library/support/StepRegistry.hpp" +#include "cucumber/cucumber-expressions/Argument.hpp" +#include "cucumber/cucumber-expressions/Errors.hpp" +#include "cucumber/cucumber-expressions/Expression.hpp" +#include "cucumber/cucumber-expressions/Matcher.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/RegularExpression.hpp" #include "cucumber/gherkin/IdGenerator.hpp" -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Errors.hpp" -#include "cucumber_cpp/library/cucumber_expression/Expression.hpp" -#include "cucumber_cpp/library/cucumber_expression/Matcher.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" -#include "cucumber_cpp/library/cucumber_expression/RegularExpression.hpp" #include "cucumber_cpp/library/support/DefinitionRegistration.hpp" #include "cucumber_cpp/library/support/StepType.hpp" #include "cucumber_cpp/library/support/SupportCodeLibrary.hpp" @@ -25,7 +25,7 @@ namespace cucumber_cpp::library::support { - StepRegistry::StepRegistry(cucumber_expression::ParameterRegistry& parameterRegistry, support::UndefinedParameters& undefinedParameters, cucumber::gherkin::IdGeneratorPtr idGenerator) + StepRegistry::StepRegistry(cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry, support::UndefinedParameters& undefinedParameters, cucumber::gherkin::IdGeneratorPtr idGenerator) : parameterRegistry{ parameterRegistry } , undefinedParameters{ undefinedParameters } , idGenerator{ std::move(idGenerator) } @@ -40,15 +40,15 @@ namespace cucumber_cpp::library::support }); } - [[nodiscard]] std::pair, std::vector>> StepRegistry::FindDefinitions(const std::string& expression) const + [[nodiscard]] std::pair, std::vector>> StepRegistry::FindDefinitions(const std::string& expression) const { - std::pair, std::vector>> result; + std::pair, std::vector>> result; result.first.reserve(idToDefinitionMap.size()); result.second.reserve(idToDefinitionMap.size()); for (const auto& [id, iter] : idToDefinitionMap) { - const auto match = std::visit(cucumber_expression::MatchVisitor{ expression }, iter->regex); + const auto match = std::visit(cucumber::cucumber_expressions::MatchVisitor{ expression }, iter->regex); if (match) { result.first.push_back(id); @@ -84,17 +84,17 @@ namespace cucumber_cpp::library::support try { auto cucumberMatcher = (matcher.starts_with('^') || matcher.ends_with('$')) - ? cucumber_expression::Matcher{ - std::in_place_type, + ? cucumber::cucumber_expressions::Matcher{ + std::in_place_type, matcher, parameterRegistry, } - : cucumber_expression::Matcher{ - std::in_place_type, + : cucumber::cucumber_expressions::Matcher{ + std::in_place_type, matcher, parameterRegistry, }; - auto cucumberMatcherType = std::holds_alternative(cucumberMatcher) + auto cucumberMatcherType = std::holds_alternative(cucumberMatcher) ? ExpressionPatternType::regularExpression : ExpressionPatternType::cucumberExpression; @@ -104,12 +104,12 @@ namespace cucumber_cpp::library::support sourceLocation.file_name(), stepType, matcher, - cucumberMatcher, + std::move(cucumberMatcher), cucumberMatcherType); idToDefinitionMap[id] = std::prev(registry.end()); } - catch (const cucumber_expression::UndefinedParameterTypeError& e) + catch (const cucumber::cucumber_expressions::UndefinedParameterTypeError& e) { undefinedParameters.definitions.emplace_back( std::string{ e.expression }, diff --git a/cucumber_cpp/library/support/StepRegistry.hpp b/cucumber_cpp/library/support/StepRegistry.hpp index f6a40abf..d2184eec 100644 --- a/cucumber_cpp/library/support/StepRegistry.hpp +++ b/cucumber_cpp/library/support/StepRegistry.hpp @@ -1,10 +1,10 @@ #ifndef CUCUMBER_CPP_STEPREGISTRY_HPP #define CUCUMBER_CPP_STEPREGISTRY_HPP +#include "cucumber/cucumber-expressions/Argument.hpp" +#include "cucumber/cucumber-expressions/Matcher.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber/gherkin/IdGenerator.hpp" -#include "cucumber_cpp/library/cucumber_expression/Argument.hpp" -#include "cucumber_cpp/library/cucumber_expression/Matcher.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/StepType.hpp" #include "cucumber_cpp/library/util/StepFactory.hpp" #include @@ -80,7 +80,7 @@ namespace cucumber_cpp::library::support StepType type; std::string pattern; - cucumber_expression::Matcher regex; + cucumber::cucumber_expressions::Matcher regex; ExpressionPatternType patternType; std::uint32_t used{ 0 }; @@ -88,20 +88,20 @@ namespace cucumber_cpp::library::support struct EntryView { - EntryView(const cucumber_expression::Matcher& stepRegex, const std::uint32_t& used) + EntryView(const cucumber::cucumber_expressions::Matcher& stepRegex, const std::uint32_t& used) : stepRegex(stepRegex) , used(used) {} - const cucumber_expression::Matcher& stepRegex; + const cucumber::cucumber_expressions::Matcher& stepRegex; const std::uint32_t& used; }; - explicit StepRegistry(cucumber_expression::ParameterRegistry& parameterRegistry, support::UndefinedParameters& undefinedParameters, cucumber::gherkin::IdGeneratorPtr idGenerator); + explicit StepRegistry(cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry, support::UndefinedParameters& undefinedParameters, cucumber::gherkin::IdGeneratorPtr idGenerator); void LoadSteps(); - [[nodiscard]] std::pair, std::vector>> FindDefinitions(const std::string& expression) const; + [[nodiscard]] std::pair, std::vector>> FindDefinitions(const std::string& expression) const; [[nodiscard]] std::size_t Size() const; @@ -113,7 +113,7 @@ namespace cucumber_cpp::library::support private: void Register(std::string id, const std::string& matcher, StepType stepType, util::StepFactory factory, std::source_location sourceLocation); - cucumber_expression::ParameterRegistry& parameterRegistry; + cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry; support::UndefinedParameters& undefinedParameters; cucumber::gherkin::IdGeneratorPtr idGenerator; diff --git a/cucumber_cpp/library/support/SupportCodeLibrary.hpp b/cucumber_cpp/library/support/SupportCodeLibrary.hpp index de7481cf..6fab7dfd 100644 --- a/cucumber_cpp/library/support/SupportCodeLibrary.hpp +++ b/cucumber_cpp/library/support/SupportCodeLibrary.hpp @@ -1,7 +1,7 @@ #ifndef SUPPORT_SUPPORT_CODE_LIBRARY_HPP #define SUPPORT_SUPPORT_CODE_LIBRARY_HPP -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/support/HookRegistry.hpp" #include "cucumber_cpp/library/support/StepRegistry.hpp" #include "cucumber_cpp/library/util/HookData.hpp" @@ -63,7 +63,7 @@ namespace cucumber_cpp::library::support { HookRegistry& hookRegistry; StepRegistry& stepRegistry; - cucumber_expression::ParameterRegistry& parameterRegistry; + cucumber::cucumber_expressions::ParameterRegistry& parameterRegistry; UndefinedParameters& undefinedParameters; }; diff --git a/cucumber_cpp/library/test/TestApplication.cpp b/cucumber_cpp/library/test/TestApplication.cpp index 532d932d..0f3a7566 100644 --- a/cucumber_cpp/library/test/TestApplication.cpp +++ b/cucumber_cpp/library/test/TestApplication.cpp @@ -57,8 +57,8 @@ namespace cucumber_cpp::library std::string stdoutString = RunWithArgs(args, static_cast>(CLI::ExitCodes::Success)); - EXPECT_THAT(stdoutString, testing::HasSubstr("1 scenarios 1 skipped")); - EXPECT_THAT(stdoutString, testing::HasSubstr("1 steps 1 skipped")); + EXPECT_THAT(stdoutString, testing::HasSubstr("1 scenario (1 skipped)")); + EXPECT_THAT(stdoutString, testing::HasSubstr("1 step (1 skipped)")); } TEST_F(TestApplication, RunFeatureFile) @@ -75,8 +75,8 @@ namespace cucumber_cpp::library std::string stdoutString = RunWithArgs(args, static_cast>(CLI::ExitCodes::Success)); - EXPECT_THAT(stdoutString, testing::HasSubstr("1 scenarios 1 passed")); - EXPECT_THAT(stdoutString, testing::HasSubstr("1 steps 1 passed")); + EXPECT_THAT(stdoutString, testing::HasSubstr("1 scenario (1 passed)")); + EXPECT_THAT(stdoutString, testing::HasSubstr("1 step (1 passed)")); } TEST_F(TestApplication, ExposeParameterRegistration) diff --git a/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.cpp b/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.cpp index f9b7d136..bbd7c02e 100644 --- a/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.cpp +++ b/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.cpp @@ -1,24 +1,22 @@ #include "cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp" +#include "cucumber/cucumber-expressions/Group.hpp" #include "cucumber/messages/Group.hpp" -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" -#include #include -#include #include #include namespace cucumber_cpp::library::util { - cucumber::messages::Group ArgumentGroupToMessageGroup(const cucumber_expression::ArgumentGroup& argumentGroup) + cucumber::messages::Group ArgumentGroupToMessageGroup(const cucumber::cucumber_expressions::ArgumentGroup& argumentGroup) { - auto messageChildren = argumentGroup.children | std::views::transform([](const auto& child) - { - return std::make_shared(ArgumentGroupToMessageGroup(child)); - }); - return { - .children = std::optional>>{ std::in_place, messageChildren.begin(), messageChildren.end() }, - .start = argumentGroup.start, - .value = argumentGroup.value, - }; + std::vector messageChildren; + for (const auto& child : argumentGroup.children) + messageChildren.push_back(ArgumentGroupToMessageGroup(child)); + + cucumber::messages::Group group; + group.children = std::move(messageChildren); + group.start = argumentGroup.start; + group.value = argumentGroup.value; + return group; } } diff --git a/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp b/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp index 3d131611..6df84dca 100644 --- a/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp +++ b/cucumber_cpp/library/util/ArgumentGroupToMessageGroup.hpp @@ -1,12 +1,12 @@ #ifndef UTIL_ARGUMENT_GROUP_TO_MESSAGE_GROUP_HPP #define UTIL_ARGUMENT_GROUP_TO_MESSAGE_GROUP_HPP +#include "cucumber/cucumber-expressions/Group.hpp" #include "cucumber/messages/Group.hpp" -#include "cucumber_cpp/library/cucumber_expression/Group.hpp" namespace cucumber_cpp::library::util { - cucumber::messages::Group ArgumentGroupToMessageGroup(const cucumber_expression::ArgumentGroup& argumentGroup); + cucumber::messages::Group ArgumentGroupToMessageGroup(const cucumber::cucumber_expressions::ArgumentGroup& argumentGroup); } #endif diff --git a/cucumber_cpp/library/util/Body.hpp b/cucumber_cpp/library/util/Body.hpp index 4667fedb..7ca8a57e 100644 --- a/cucumber_cpp/library/util/Body.hpp +++ b/cucumber_cpp/library/util/Body.hpp @@ -1,7 +1,7 @@ #ifndef UTIL_BODY_HPP #define UTIL_BODY_HPP -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber_cpp/library/util/TestStepResult.hpp" #include "gtest/gtest.h" #include @@ -49,7 +49,7 @@ namespace cucumber_cpp::library::util struct Argument { std::string converterName; - cucumber_expression::ConvertFunctionArg converterArgs; + cucumber::cucumber_expressions::ConvertFunctionArg converterArgs; }; using ExecuteArgs = std::vector; diff --git a/cucumber_cpp/library/util/Broadcaster.cpp b/cucumber_cpp/library/util/Broadcaster.cpp index 7b00c44d..6456aff5 100644 --- a/cucumber_cpp/library/util/Broadcaster.cpp +++ b/cucumber_cpp/library/util/Broadcaster.cpp @@ -1,28 +1,6 @@ #include "cucumber_cpp/library/util/Broadcaster.hpp" -#include "cucumber/messages/Attachment.hpp" #include "cucumber/messages/Envelope.hpp" -#include "cucumber/messages/ExternalAttachment.hpp" -#include "cucumber/messages/GherkinDocument.hpp" -#include "cucumber/messages/Hook.hpp" -#include "cucumber/messages/Meta.hpp" -#include "cucumber/messages/ParameterType.hpp" -#include "cucumber/messages/ParseError.hpp" -#include "cucumber/messages/Pickle.hpp" -#include "cucumber/messages/Source.hpp" -#include "cucumber/messages/StepDefinition.hpp" -#include "cucumber/messages/Suggestion.hpp" -#include "cucumber/messages/TestCase.hpp" -#include "cucumber/messages/TestCaseFinished.hpp" -#include "cucumber/messages/TestCaseStarted.hpp" -#include "cucumber/messages/TestRunFinished.hpp" -#include "cucumber/messages/TestRunHookFinished.hpp" -#include "cucumber/messages/TestRunHookStarted.hpp" -#include "cucumber/messages/TestRunStarted.hpp" -#include "cucumber/messages/TestStepFinished.hpp" -#include "cucumber/messages/TestStepStarted.hpp" -#include "cucumber/messages/UndefinedParameterType.hpp" #include -#include #include #include @@ -56,156 +34,10 @@ namespace cucumber_cpp::library::util std::erase(listeners, listener); } - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.attachment = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.externalAttachment = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.gherkinDocument = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.hook = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.meta = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.parameterType = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.parseError = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.pickle = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.suggestion = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.source = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.stepDefinition = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testCase = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testCaseFinished = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testCaseStarted = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testRunFinished = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testRunStarted = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testStepFinished = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testStepStarted = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testRunHookStarted = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.testRunHookFinished = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(std::shared_ptr message) const - { - cucumber::messages::Envelope envelope; - envelope.undefinedParameterType = std::move(message); - BroadcastEvent(envelope); - } - - void Broadcaster::BroadcastEvent(const cucumber::messages::Envelope& envelope) const + void Broadcaster::Dispatch(cucumber::messages::Envelope&& envelope) { + const auto& stored = archive.Store(std::move(envelope)); for (const auto& listener : listeners) - listener->Invoke(envelope); + listener->Invoke(stored); } } diff --git a/cucumber_cpp/library/util/Broadcaster.hpp b/cucumber_cpp/library/util/Broadcaster.hpp index 6da12a16..47fc7431 100644 --- a/cucumber_cpp/library/util/Broadcaster.hpp +++ b/cucumber_cpp/library/util/Broadcaster.hpp @@ -23,8 +23,9 @@ #include "cucumber/messages/TestStepFinished.hpp" #include "cucumber/messages/TestStepStarted.hpp" #include "cucumber/messages/UndefinedParameterType.hpp" +#include "cucumber/query/EnvelopeArchive.hpp" #include -#include +#include #include namespace cucumber::messages @@ -45,7 +46,7 @@ namespace cucumber_cpp::library::util Listener(Listener&&) = delete; Listener& operator=(Listener&&) = delete; - ~Listener(); + virtual ~Listener(); void Invoke(const cucumber::messages::Envelope& envelope) const; @@ -59,31 +60,18 @@ namespace cucumber_cpp::library::util void AddListener(Listener* listener); void RemoveListener(Listener* listener); - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; - void BroadcastEvent(std::shared_ptr message) const; + template + void BroadcastEvent(Populate&& populate) + { + cucumber::messages::Envelope envelope; + std::forward(populate)(envelope); + Dispatch(std::move(envelope)); + } private: - void BroadcastEvent(const cucumber::messages::Envelope& envelope) const; + void Dispatch(cucumber::messages::Envelope&& envelope); + cucumber::query::EnvelopeArchive archive; std::vector listeners; }; } diff --git a/cucumber_cpp/library/util/CMakeLists.txt b/cucumber_cpp/library/util/CMakeLists.txt index a0d32e62..5de55da5 100644 --- a/cucumber_cpp/library/util/CMakeLists.txt +++ b/cucumber_cpp/library/util/CMakeLists.txt @@ -59,7 +59,9 @@ target_include_directories(cucumber_cpp.library.util PUBLIC ) target_link_libraries(cucumber_cpp.library.util PUBLIC - cucumber_gherkin_lib + cucumber::cucumber_expressions + cucumber::gherkin + cucumber::query fmt::fmt GTest::gtest ) diff --git a/cucumber_cpp/library/util/Timestamp.cpp b/cucumber_cpp/library/util/Timestamp.cpp index ff5a1e6f..92cc800a 100644 --- a/cucumber_cpp/library/util/Timestamp.cpp +++ b/cucumber_cpp/library/util/Timestamp.cpp @@ -3,9 +3,9 @@ #include "cucumber/messages/Duration.hpp" #include "cucumber/messages/Timestamp.hpp" #include "cucumber_cpp/library/util/Duration.hpp" -#include "fmt/chrono.h" #include "fmt/format.h" #include +#include #include namespace cucumber_cpp::library::util @@ -70,8 +70,23 @@ namespace cucumber_cpp::library::util std::string MakeIso8601Timestamp(const cucumber::messages::Timestamp& timestamp) { - const auto duration = std::chrono::duration_cast(std::chrono::seconds(timestamp.seconds) + std::chrono::nanoseconds(timestamp.nanos)); - const std::chrono::system_clock::time_point tp{ duration }; - return fmt::format("{:%FT%T%Z}", tp); + const std::time_t seconds = timestamp.seconds; + std::tm utcTime{}; + #ifdef _WIN32 + gmtime_s(&utcTime, &seconds); + #else + gmtime_r(&seconds, &utcTime); + #endif + const auto timestampWithoutFraction = fmt::format( + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}", + utcTime.tm_year + 1900, + utcTime.tm_mon + 1, + utcTime.tm_mday, + utcTime.tm_hour, + utcTime.tm_min, + utcTime.tm_sec); + if (timestamp.nanos == 0) + return timestampWithoutFraction + "Z"; + return fmt::format("{}.{:09}Z", timestampWithoutFraction, timestamp.nanos); } } diff --git a/cucumber_cpp/library/util/TransformArgument.cpp b/cucumber_cpp/library/util/TransformArgument.cpp index 29e75b7a..226065a3 100644 --- a/cucumber_cpp/library/util/TransformArgument.cpp +++ b/cucumber_cpp/library/util/TransformArgument.cpp @@ -1,8 +1,8 @@ #include "cucumber_cpp/library/util/TransformArgument.hpp" +#include "cucumber/cucumber-expressions/ParameterRegistry.hpp" #include "cucumber/messages/Group.hpp" #include "cucumber/messages/StepMatchArgument.hpp" -#include "cucumber_cpp/library/cucumber_expression/ParameterRegistry.hpp" #include "cucumber_cpp/library/util/Body.hpp" #include #include @@ -17,14 +17,14 @@ namespace cucumber_cpp::library::util return group.value; } - cucumber_expression::ConvertFunctionArg GroupToArgumentGroup(const cucumber::messages::Group& group) + cucumber::cucumber_expressions::ConvertFunctionArg GroupToArgumentGroup(const cucumber::messages::Group& group) { if (!group.children.has_value() || group.children->empty()) return { group.value }; auto strings = group.children.value() | std::views::transform([](const auto& child) { - return ToString(*child); + return ToString(child); }); return { strings.begin(), strings.end() }; @@ -35,7 +35,7 @@ namespace cucumber_cpp::library::util { return { .converterName = argument.parameterTypeName.value_or(""), - .converterArgs = GroupToArgumentGroup(*argument.group), + .converterArgs = GroupToArgumentGroup(argument.group), }; } } diff --git a/cucumber_cpp/library/util/TransformHookData.cpp b/cucumber_cpp/library/util/TransformHookData.cpp index afbfe668..2f3c66fd 100644 --- a/cucumber_cpp/library/util/TransformHookData.cpp +++ b/cucumber_cpp/library/util/TransformHookData.cpp @@ -41,17 +41,21 @@ namespace cucumber_cpp::library::util cucumber::messages::Hook TransformHookData(const HookData& hookData) { - return { - .id = hookData.id, - .name = hookData.name.has_value() ? std::make_optional(hookData.name.value()) : std::nullopt, - .sourceReference = std::make_shared(cucumber::messages::SourceReference{ - .uri = hookData.sourceLocation.file_name(), - .location = std::make_shared(cucumber::messages::Location{ - .line = hookData.sourceLocation.line(), - }), - }), - .tagExpression = hookData.expression.has_value() ? std::make_optional(hookData.expression.value()) : std::nullopt, - .type = MapHookType(hookData.type), - }; + cucumber::messages::Location location; + location.line = hookData.sourceLocation.line(); + + cucumber::messages::SourceReference sourceReference; + sourceReference.uri = hookData.sourceLocation.file_name(); + sourceReference.location = location; + + cucumber::messages::Hook hook; + hook.id = hookData.id; + if (hookData.name.has_value()) + hook.name = hookData.name.value(); + hook.sourceReference = sourceReference; + if (hookData.expression.has_value()) + hook.tagExpression = hookData.expression.value(); + hook.type = MapHookType(hookData.type); + return hook; } } diff --git a/cucumber_cpp/library/util/TransformPickleTag.cpp b/cucumber_cpp/library/util/TransformPickleTag.cpp index 9053b769..32ab548d 100644 --- a/cucumber_cpp/library/util/TransformPickleTag.cpp +++ b/cucumber_cpp/library/util/TransformPickleTag.cpp @@ -11,11 +11,11 @@ namespace cucumber_cpp::library::util { - std::set> TransformPickleTags(std::span> tags) + std::set> TransformPickleTags(std::span tags) { auto tagNames = tags | std::views::transform([](const auto& tag) -> const std::string& { - return tag->name; + return tag.name; }); return { std::begin(tagNames), std::end(tagNames) }; } diff --git a/cucumber_cpp/library/util/TransformPickleTag.hpp b/cucumber_cpp/library/util/TransformPickleTag.hpp index 978f98bb..fb16cc2b 100644 --- a/cucumber_cpp/library/util/TransformPickleTag.hpp +++ b/cucumber_cpp/library/util/TransformPickleTag.hpp @@ -10,7 +10,7 @@ namespace cucumber_cpp::library::util { - std::set> TransformPickleTags(std::span> tags); + std::set> TransformPickleTags(std::span tags); } #endif diff --git a/cucumber_cpp/library/util/TransformStepMatchArgumentsList.cpp b/cucumber_cpp/library/util/TransformStepMatchArgumentsList.cpp index 8754a05e..a56dab9b 100644 --- a/cucumber_cpp/library/util/TransformStepMatchArgumentsList.cpp +++ b/cucumber_cpp/library/util/TransformStepMatchArgumentsList.cpp @@ -10,7 +10,7 @@ namespace cucumber_cpp::library::util { auto strings = args.stepMatchArguments | std::views::transform([](const auto& arg) { - return util::ToArgument(*arg); + return util::ToArgument(arg); }); return { strings.begin(), strings.end() }; } diff --git a/cucumber_cpp/library/util/TransformTable.cpp b/cucumber_cpp/library/util/TransformTable.cpp index ab3efb03..60d88958 100644 --- a/cucumber_cpp/library/util/TransformTable.cpp +++ b/cucumber_cpp/library/util/TransformTable.cpp @@ -18,10 +18,10 @@ namespace cucumber_cpp::library::util for (const auto& pickleTableRow : pickleTable->rows) { TableRow& tableRow = table.rows.emplace_back(); - tableRow.cells.reserve(pickleTableRow->cells.size()); + tableRow.cells.reserve(pickleTableRow.cells.size()); - for (const auto& cell : pickleTableRow->cells) - tableRow.cells.emplace_back(cell->value); + for (const auto& cell : pickleTableRow.cells) + tableRow.cells.emplace_back(cell.value); } return table; @@ -36,11 +36,15 @@ namespace cucumber_cpp::library::util for (const auto& tableRow : table->rows) { - const auto& pickleTableRow = pickleTable.rows.emplace_back(std::make_shared()); - pickleTableRow->cells.reserve(tableRow.cells.size()); + cucumber::messages::PickleTableRow& pickleTableRow = pickleTable.rows.emplace_back(); + pickleTableRow.cells.reserve(tableRow.cells.size()); for (const auto& cell : tableRow.cells) - pickleTableRow->cells.emplace_back(std::make_shared(cucumber::messages::PickleTableCell{ .value = cell.value })); + { + cucumber::messages::PickleTableCell pickleTableCell; + pickleTableCell.value = cell.value; + pickleTableRow.cells.push_back(pickleTableCell); + } } return pickleTable; diff --git a/cucumber_cpp/library/util/TransformTag.cpp b/cucumber_cpp/library/util/TransformTag.cpp index 34ea40c4..98449dee 100644 --- a/cucumber_cpp/library/util/TransformTag.cpp +++ b/cucumber_cpp/library/util/TransformTag.cpp @@ -11,11 +11,11 @@ namespace cucumber_cpp::library::util { - std::set> TransformTags(std::span> tags) + std::set> TransformTags(std::span tags) { auto tagNames = tags | std::views::transform([](const auto& tag) -> const std::string& { - return tag->name; + return tag.name; }); return { std::begin(tagNames), std::end(tagNames) }; } diff --git a/cucumber_cpp/library/util/TransformTag.hpp b/cucumber_cpp/library/util/TransformTag.hpp index e5a94b53..5e60b6fd 100644 --- a/cucumber_cpp/library/util/TransformTag.hpp +++ b/cucumber_cpp/library/util/TransformTag.hpp @@ -10,7 +10,7 @@ namespace cucumber_cpp::library::util { - std::set> TransformTags(std::span> tags); + std::set> TransformTags(std::span tags); } #endif diff --git a/cucumber_cpp/library/util/TransformTestStepResult.cpp b/cucumber_cpp/library/util/TransformTestStepResult.cpp index 74f5b148..d7e28e8a 100644 --- a/cucumber_cpp/library/util/TransformTestStepResult.cpp +++ b/cucumber_cpp/library/util/TransformTestStepResult.cpp @@ -11,14 +11,21 @@ namespace cucumber_cpp::library::util { cucumber::messages::TestStepResult TransformTestStepResult(util::TestStepResult result) { - return { - .duration = std::make_shared(cucumber::messages::Duration{ - .seconds = result.duration.seconds, - .nanos = result.duration.nanos, - }), - .message = result.message, - .status = util::TransformTestStepResultStatus(result.status), - .exception = result.exception.has_value() ? std::make_optional(std::make_shared(cucumber::messages::Exception{ .type = result.exception->type, .message = result.exception->message })) : std::nullopt, - }; + cucumber::messages::Duration duration; + duration.seconds = result.duration.seconds; + duration.nanos = result.duration.nanos; + + cucumber::messages::TestStepResult testStepResult; + testStepResult.duration = duration; + testStepResult.message = result.message; + testStepResult.status = util::TransformTestStepResultStatus(result.status); + if (result.exception.has_value()) + { + cucumber::messages::Exception exception; + exception.type = result.exception->type; + exception.message = result.exception->message; + testStepResult.exception = exception; + } + return testStepResult; } } diff --git a/cucumber_cpp/library/util/test/TestBroadcaster.cpp b/cucumber_cpp/library/util/test/TestBroadcaster.cpp index 9313782b..71c8b660 100644 --- a/cucumber_cpp/library/util/test/TestBroadcaster.cpp +++ b/cucumber_cpp/library/util/test/TestBroadcaster.cpp @@ -21,8 +21,14 @@ namespace cucumber_cpp::library::util sawMeta = sawMeta || envelope.meta.has_value(); } }; - broadcaster.BroadcastEvent(std::make_shared()); - broadcaster.BroadcastEvent(std::make_shared()); + broadcaster.BroadcastEvent([](cucumber::messages::Envelope& envelope) + { + envelope.externalAttachment = cucumber::messages::ExternalAttachment{}; + }); + broadcaster.BroadcastEvent([](cucumber::messages::Envelope& envelope) + { + envelope.meta = cucumber::messages::Meta{}; + }); EXPECT_THAT(sawExternalAttachment, IsTrue()); EXPECT_THAT(sawMeta, IsTrue()); diff --git a/cucumber_cpp/library/util/test/TestTransformTable.cpp b/cucumber_cpp/library/util/test/TestTransformTable.cpp index d80afbc9..9db30b39 100644 --- a/cucumber_cpp/library/util/test/TestTransformTable.cpp +++ b/cucumber_cpp/library/util/test/TestTransformTable.cpp @@ -28,9 +28,9 @@ namespace cucumber_cpp::library::util ASSERT_THAT(pickle.has_value(), IsTrue()); ASSERT_THAT(pickle->rows, SizeIs(1)); - ASSERT_THAT(pickle->rows[0]->cells, SizeIs(2)); - EXPECT_THAT(pickle->rows[0]->cells[0]->value, Eq("a")); - EXPECT_THAT(pickle->rows[0]->cells[1]->value, Eq("b")); + ASSERT_THAT(pickle->rows[0].cells, SizeIs(2)); + EXPECT_THAT(pickle->rows[0].cells[0].value, Eq("a")); + EXPECT_THAT(pickle->rows[0].cells[1].value, Eq("b")); const std::optional back = TransformTable(pickle); diff --git a/cucumber_cpp/library/util/test/TestTransformTag.cpp b/cucumber_cpp/library/util/test/TestTransformTag.cpp index c9053475..56959566 100644 --- a/cucumber_cpp/library/util/test/TestTransformTag.cpp +++ b/cucumber_cpp/library/util/test/TestTransformTag.cpp @@ -11,17 +11,17 @@ namespace cucumber_cpp::library::util namespace { - std::shared_ptr MakeTag(std::string name) + cucumber::messages::Tag MakeTag(std::string name) { - auto tag = std::make_shared(); - tag->name = std::move(name); + cucumber::messages::Tag tag; + tag.name = std::move(name); return tag; } } TEST(TransformTag, collects_unique_sorted_names) { - const std::vector> tags{ MakeTag("@b"), MakeTag("@a"), MakeTag("@b") }; + const std::vector tags{ MakeTag("@b"), MakeTag("@a"), MakeTag("@b") }; const auto names = TransformTags(tags); diff --git a/testdata/cucumber-expression/matching/allows-escaped-optional-parameter-types.yaml b/testdata/cucumber-expression/matching/allows-escaped-optional-parameter-types.yaml deleted file mode 100644 index f7773b3a..00000000 --- a/testdata/cucumber-expression/matching/allows-escaped-optional-parameter-types.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "\\({int})" -text: "(3)" -expected_args: -- 3 diff --git a/testdata/cucumber-expression/matching/allows-parameter-type-in-alternation-1.yaml b/testdata/cucumber-expression/matching/allows-parameter-type-in-alternation-1.yaml deleted file mode 100644 index 751db4a0..00000000 --- a/testdata/cucumber-expression/matching/allows-parameter-type-in-alternation-1.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: a/i{int}n/y -text: i18n -expected_args: -- 18 diff --git a/testdata/cucumber-expression/matching/allows-parameter-type-in-alternation-2.yaml b/testdata/cucumber-expression/matching/allows-parameter-type-in-alternation-2.yaml deleted file mode 100644 index 74327e30..00000000 --- a/testdata/cucumber-expression/matching/allows-parameter-type-in-alternation-2.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: a/i{int}n/y -text: a11y -expected_args: -- 11 diff --git a/testdata/cucumber-expression/matching/does-allow-parameter-adjacent-to-alternation.yaml b/testdata/cucumber-expression/matching/does-allow-parameter-adjacent-to-alternation.yaml deleted file mode 100644 index 228061c3..00000000 --- a/testdata/cucumber-expression/matching/does-allow-parameter-adjacent-to-alternation.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{int}st/nd/rd/th" -text: 3rd -expected_args: -- 3 diff --git a/testdata/cucumber-expression/matching/does-not-allow-alternation-in-optional.yaml b/testdata/cucumber-expression/matching/does-not-allow-alternation-in-optional.yaml deleted file mode 100644 index f15f8af9..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-alternation-in-optional.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: three( brown/black) mice -text: three brown/black mice -exception: |- - This Cucumber Expression has a problem at column 13: - - three( brown/black) mice - ^ - An alternation can not be used inside an optional. - If you did not mean to use an alternation you can use '\/' to escape the '/'. Otherwise rephrase your expression or consider using a regular expression instead. diff --git a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-left-parameter.yaml b/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-left-parameter.yaml deleted file mode 100644 index 8eb0698a..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-left-parameter.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: "{int}/x" -text: '3' -exception: |- - This Cucumber Expression has a problem at column 6: - - {int}/x - ^ - Alternative may not be empty. - If you did not mean to use an alternative you can use '\/' to escape the '/' diff --git a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-optional.yaml b/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-optional.yaml deleted file mode 100644 index 0048310a..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-optional.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: three (brown)/black mice -text: three brown mice -exception: |- - This Cucumber Expression has a problem at column 7: - - three (brown)/black mice - ^-----^ - An alternative may not exclusively contain optionals. - If you did not mean to use an optional you can use '\(' to escape the '(' diff --git a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-right-parameter.yaml b/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-right-parameter.yaml deleted file mode 100644 index d32f2f57..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative-by-adjacent-right-parameter.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: x/{int} -text: '3' -exception: |- - This Cucumber Expression has a problem at column 3: - - x/{int} - ^ - Alternative may not be empty. - If you did not mean to use an alternative you can use '\/' to escape the '/' diff --git a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative.yaml b/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative.yaml deleted file mode 100644 index 335c3058..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-alternation-with-empty-alternative.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: three brown//black mice -text: three brown mice -exception: |- - This Cucumber Expression has a problem at column 13: - - three brown//black mice - ^ - Alternative may not be empty. - If you did not mean to use an alternative you can use '\/' to escape the '/' diff --git a/testdata/cucumber-expression/matching/does-not-allow-empty-optional.yaml b/testdata/cucumber-expression/matching/does-not-allow-empty-optional.yaml deleted file mode 100644 index 3921c499..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-empty-optional.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: three () mice -text: three brown mice -exception: |- - This Cucumber Expression has a problem at column 7: - - three () mice - ^^ - An optional must contain some text. - If you did not mean to use an optional you can use '\(' to escape the '(' diff --git a/testdata/cucumber-expression/matching/does-not-allow-nested-optional.yaml b/testdata/cucumber-expression/matching/does-not-allow-nested-optional.yaml deleted file mode 100644 index 96ac0492..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-nested-optional.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: "(a(b))" -exception: |- - This Cucumber Expression has a problem at column 3: - - (a(b)) - ^-^ - An optional may not contain an other optional. - If you did not mean to use an optional type you can use '\(' to escape the '('. For more complicated expressions consider using a regular expression instead. diff --git a/testdata/cucumber-expression/matching/does-not-allow-optional-parameter-types.yaml b/testdata/cucumber-expression/matching/does-not-allow-optional-parameter-types.yaml deleted file mode 100644 index 4a50670b..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-optional-parameter-types.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: "({int})" -text: '3' -exception: |- - This Cucumber Expression has a problem at column 2: - - ({int}) - ^---^ - An optional may not contain a parameter type. - If you did not mean to use an parameter type you can use '\{' to escape the '{' diff --git a/testdata/cucumber-expression/matching/does-not-allow-parameter-name-with-reserved-characters.yaml b/testdata/cucumber-expression/matching/does-not-allow-parameter-name-with-reserved-characters.yaml deleted file mode 100644 index fa752f0c..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-parameter-name-with-reserved-characters.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: "{(string)}" -text: something -exception: |- - This Cucumber Expression has a problem at column 2: - - {(string)} - ^ - Parameter names may not contain '{', '}', '(', ')', '\' or '/'. - Did you mean to use a regular expression? diff --git a/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-1.yaml b/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-1.yaml deleted file mode 100644 index 774285fb..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-1.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: three (exceptionally\) {string\} mice -exception: |- - This Cucumber Expression has a problem at column 24: - - three (exceptionally\) {string\} mice - ^ - The '{' does not have a matching '}'. - If you did not intend to use a parameter you can use '\{' to escape the a parameter diff --git a/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-2.yaml b/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-2.yaml deleted file mode 100644 index 19eab961..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-2.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: three (exceptionally\) {string} mice -exception: |- - This Cucumber Expression has a problem at column 7: - - three (exceptionally\) {string} mice - ^ - The '(' does not have a matching ')'. - If you did not intend to use optional text you can use '\(' to escape the optional text diff --git a/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-3.yaml b/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-3.yaml deleted file mode 100644 index 7ed2b7e2..00000000 --- a/testdata/cucumber-expression/matching/does-not-allow-unfinished-parenthesis-3.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: three ((exceptionally\) strong) mice -exception: |- - This Cucumber Expression has a problem at column 7: - - three ((exceptionally\) strong) mice - ^ - The '(' does not have a matching ')'. - If you did not intend to use optional text you can use '\(' to escape the optional text diff --git a/testdata/cucumber-expression/matching/does-not-match-misquoted-string.yaml b/testdata/cucumber-expression/matching/does-not-match-misquoted-string.yaml deleted file mode 100644 index 48f92b19..00000000 --- a/testdata/cucumber-expression/matching/does-not-match-misquoted-string.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: three {string} mice -text: three "blind' mice -expected_args: diff --git a/testdata/cucumber-expression/matching/doesnt-match-float-as-int.yaml b/testdata/cucumber-expression/matching/doesnt-match-float-as-int.yaml deleted file mode 100644 index 1df15a97..00000000 --- a/testdata/cucumber-expression/matching/doesnt-match-float-as-int.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: "{int}" -text: '1.22' -expected_args: diff --git a/testdata/cucumber-expression/matching/matches-alternation.yaml b/testdata/cucumber-expression/matching/matches-alternation.yaml deleted file mode 100644 index 38a4b774..00000000 --- a/testdata/cucumber-expression/matching/matches-alternation.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: mice/rats and rats\/mice -text: rats and rats/mice -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-anonymous-parameter-type.yaml b/testdata/cucumber-expression/matching/matches-anonymous-parameter-type.yaml deleted file mode 100644 index 19e2c9a1..00000000 --- a/testdata/cucumber-expression/matching/matches-anonymous-parameter-type.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{}" -text: '0.22' -expected_args: -- '0.22' diff --git a/testdata/cucumber-expression/matching/matches-bigdecimal.yaml b/testdata/cucumber-expression/matching/matches-bigdecimal.yaml deleted file mode 100644 index 6c08f243..00000000 --- a/testdata/cucumber-expression/matching/matches-bigdecimal.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{bigdecimal}" -text: '3.1415926535897932384626433832795028841971693993751' -expected_args: -- '3.1415926535897932384626433832795028841971693993751' diff --git a/testdata/cucumber-expression/matching/matches-biginteger-does-not-work-with-cpp.yaml b/testdata/cucumber-expression/matching/matches-biginteger-does-not-work-with-cpp.yaml deleted file mode 100644 index a57ba798..00000000 --- a/testdata/cucumber-expression/matching/matches-biginteger-does-not-work-with-cpp.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{biginteger}" -text: '9223372036854775807' -expected_args: -- '9223372036854775807' diff --git a/testdata/cucumber-expression/matching/matches-byte.yaml b/testdata/cucumber-expression/matching/matches-byte.yaml deleted file mode 100644 index 8973421e..00000000 --- a/testdata/cucumber-expression/matching/matches-byte.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{byte}" -text: '127' -expected_args: -- 127 diff --git a/testdata/cucumber-expression/matching/matches-double-quoted-empty-string-as-empty-string-along-with-other-strings.yaml b/testdata/cucumber-expression/matching/matches-double-quoted-empty-string-as-empty-string-along-with-other-strings.yaml deleted file mode 100644 index 5370b3c9..00000000 --- a/testdata/cucumber-expression/matching/matches-double-quoted-empty-string-as-empty-string-along-with-other-strings.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -expression: three {string} and {string} mice -text: three "" and "handsome" mice -expected_args: -- '' -- handsome diff --git a/testdata/cucumber-expression/matching/matches-double-quoted-empty-string-as-empty-string.yaml b/testdata/cucumber-expression/matching/matches-double-quoted-empty-string-as-empty-string.yaml deleted file mode 100644 index b884fb3d..00000000 --- a/testdata/cucumber-expression/matching/matches-double-quoted-empty-string-as-empty-string.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three "" mice -expected_args: -- '' diff --git a/testdata/cucumber-expression/matching/matches-double-quoted-string-with-escaped-double-quote.yaml b/testdata/cucumber-expression/matching/matches-double-quoted-string-with-escaped-double-quote.yaml deleted file mode 100644 index f1d296ce..00000000 --- a/testdata/cucumber-expression/matching/matches-double-quoted-string-with-escaped-double-quote.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three "bl\"nd" mice -expected_args: -- bl"nd diff --git a/testdata/cucumber-expression/matching/matches-double-quoted-string-with-single-quotes.yaml b/testdata/cucumber-expression/matching/matches-double-quoted-string-with-single-quotes.yaml deleted file mode 100644 index 544c34b9..00000000 --- a/testdata/cucumber-expression/matching/matches-double-quoted-string-with-single-quotes.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three "'blind'" mice -expected_args: -- "'blind'" diff --git a/testdata/cucumber-expression/matching/matches-double-quoted-string.yaml b/testdata/cucumber-expression/matching/matches-double-quoted-string.yaml deleted file mode 100644 index cda1c3da..00000000 --- a/testdata/cucumber-expression/matching/matches-double-quoted-string.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three "blind" mice -expected_args: -- blind diff --git a/testdata/cucumber-expression/matching/matches-double.yaml b/testdata/cucumber-expression/matching/matches-double.yaml deleted file mode 100644 index a0f908fa..00000000 --- a/testdata/cucumber-expression/matching/matches-double.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{double}" -text: '3.141592653589793' -expected_args: -- 3.141592653589793 diff --git a/testdata/cucumber-expression/matching/matches-doubly-escaped-parenthesis.yaml b/testdata/cucumber-expression/matching/matches-doubly-escaped-parenthesis.yaml deleted file mode 100644 index 7b957dfe..00000000 --- a/testdata/cucumber-expression/matching/matches-doubly-escaped-parenthesis.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three \\(exceptionally) \\{string} mice -text: three \exceptionally \"blind" mice -expected_args: -- blind diff --git a/testdata/cucumber-expression/matching/matches-doubly-escaped-slash-1.yaml b/testdata/cucumber-expression/matching/matches-doubly-escaped-slash-1.yaml deleted file mode 100644 index 4ef1e22a..00000000 --- a/testdata/cucumber-expression/matching/matches-doubly-escaped-slash-1.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: 12\\/2020 -text: 12\ -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-doubly-escaped-slash-2.yaml b/testdata/cucumber-expression/matching/matches-doubly-escaped-slash-2.yaml deleted file mode 100644 index b54abc9d..00000000 --- a/testdata/cucumber-expression/matching/matches-doubly-escaped-slash-2.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: 12\\/2020 -text: '2020' -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-escaped-parenthesis-1.yaml b/testdata/cucumber-expression/matching/matches-escaped-parenthesis-1.yaml deleted file mode 100644 index d5e2abe2..00000000 --- a/testdata/cucumber-expression/matching/matches-escaped-parenthesis-1.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: three \(exceptionally) \{string} mice -text: three (exceptionally) {string} mice -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-escaped-parenthesis-2.yaml b/testdata/cucumber-expression/matching/matches-escaped-parenthesis-2.yaml deleted file mode 100644 index 356226ad..00000000 --- a/testdata/cucumber-expression/matching/matches-escaped-parenthesis-2.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three \((exceptionally)) \{{string}} mice -text: three (exceptionally) {"blind"} mice -expected_args: -- blind diff --git a/testdata/cucumber-expression/matching/matches-escaped-parenthesis-3.yaml b/testdata/cucumber-expression/matching/matches-escaped-parenthesis-3.yaml deleted file mode 100644 index 356226ad..00000000 --- a/testdata/cucumber-expression/matching/matches-escaped-parenthesis-3.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three \((exceptionally)) \{{string}} mice -text: three (exceptionally) {"blind"} mice -expected_args: -- blind diff --git a/testdata/cucumber-expression/matching/matches-escaped-slash.yaml b/testdata/cucumber-expression/matching/matches-escaped-slash.yaml deleted file mode 100644 index 43950d5a..00000000 --- a/testdata/cucumber-expression/matching/matches-escaped-slash.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: 12\/2020 -text: 12/2020 -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-float-with-integer-part.yaml b/testdata/cucumber-expression/matching/matches-float-with-integer-part.yaml deleted file mode 100644 index a6e394b7..00000000 --- a/testdata/cucumber-expression/matching/matches-float-with-integer-part.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{float}" -text: '0.22' -expected_args: -- 0.22 diff --git a/testdata/cucumber-expression/matching/matches-float-without-integer-part.yaml b/testdata/cucumber-expression/matching/matches-float-without-integer-part.yaml deleted file mode 100644 index 4511fc7c..00000000 --- a/testdata/cucumber-expression/matching/matches-float-without-integer-part.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{float}" -text: ".22" -expected_args: -- 0.22 diff --git a/testdata/cucumber-expression/matching/matches-float.yaml b/testdata/cucumber-expression/matching/matches-float.yaml deleted file mode 100644 index 0832e281..00000000 --- a/testdata/cucumber-expression/matching/matches-float.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{float}" -text: '3.141593' -expected_args: -- 3.141593 diff --git a/testdata/cucumber-expression/matching/matches-int.yaml b/testdata/cucumber-expression/matching/matches-int.yaml deleted file mode 100644 index 91d6f151..00000000 --- a/testdata/cucumber-expression/matching/matches-int.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{int}" -text: '2147483647' -expected_args: -- 2147483647 diff --git a/testdata/cucumber-expression/matching/matches-long.yaml b/testdata/cucumber-expression/matching/matches-long.yaml deleted file mode 100644 index 06c872d2..00000000 --- a/testdata/cucumber-expression/matching/matches-long.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{long}" -text: '9223372036854775807' -expected_args: -- 9223372036854775807 diff --git a/testdata/cucumber-expression/matching/matches-multiple-double-quoted-strings.yaml b/testdata/cucumber-expression/matching/matches-multiple-double-quoted-strings.yaml deleted file mode 100644 index eb032a44..00000000 --- a/testdata/cucumber-expression/matching/matches-multiple-double-quoted-strings.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -expression: three {string} and {string} mice -text: three "blind" and "crippled" mice -expected_args: -- blind -- crippled diff --git a/testdata/cucumber-expression/matching/matches-multiple-single-quoted-strings.yaml b/testdata/cucumber-expression/matching/matches-multiple-single-quoted-strings.yaml deleted file mode 100644 index 3a3c4783..00000000 --- a/testdata/cucumber-expression/matching/matches-multiple-single-quoted-strings.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -expression: three {string} and {string} mice -text: three 'blind' and 'crippled' mice -expected_args: -- blind -- crippled diff --git a/testdata/cucumber-expression/matching/matches-optional-before-alternation-1.yaml b/testdata/cucumber-expression/matching/matches-optional-before-alternation-1.yaml deleted file mode 100644 index 01ab2a6f..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-before-alternation-1.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: three (brown )mice/rats -text: three brown mice -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-optional-before-alternation-2.yaml b/testdata/cucumber-expression/matching/matches-optional-before-alternation-2.yaml deleted file mode 100644 index e2ad6b68..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-before-alternation-2.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: three (brown )mice/rats -text: three rats -expected_args: [] diff --git a/testdata/cucumber-expression/matching/matches-optional-before-alternation-with-regex-characters-1.yaml b/testdata/cucumber-expression/matching/matches-optional-before-alternation-with-regex-characters-1.yaml deleted file mode 100644 index a21e00e9..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-before-alternation-with-regex-characters-1.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: I wait {int} second(s)./second(s)? -text: I wait 2 seconds? -expected_args: -- 2 diff --git a/testdata/cucumber-expression/matching/matches-optional-before-alternation-with-regex-characters-2.yaml b/testdata/cucumber-expression/matching/matches-optional-before-alternation-with-regex-characters-2.yaml deleted file mode 100644 index e255c953..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-before-alternation-with-regex-characters-2.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: I wait {int} second(s)./second(s)? -text: I wait 1 second. -expected_args: -- 1 diff --git a/testdata/cucumber-expression/matching/matches-optional-in-alternation-1.yaml b/testdata/cucumber-expression/matching/matches-optional-in-alternation-1.yaml deleted file mode 100644 index 6d878c1f..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-in-alternation-1.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{int} rat(s)/mouse/mice" -text: 3 rats -expected_args: -- 3 diff --git a/testdata/cucumber-expression/matching/matches-optional-in-alternation-2.yaml b/testdata/cucumber-expression/matching/matches-optional-in-alternation-2.yaml deleted file mode 100644 index c16a3912..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-in-alternation-2.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{int} rat(s)/mouse/mice" -text: 2 mice -expected_args: -- 2 diff --git a/testdata/cucumber-expression/matching/matches-optional-in-alternation-3.yaml b/testdata/cucumber-expression/matching/matches-optional-in-alternation-3.yaml deleted file mode 100644 index 5ac8f5e2..00000000 --- a/testdata/cucumber-expression/matching/matches-optional-in-alternation-3.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{int} rat(s)/mouse/mice" -text: 1 mouse -expected_args: -- 1 diff --git a/testdata/cucumber-expression/matching/matches-short.yaml b/testdata/cucumber-expression/matching/matches-short.yaml deleted file mode 100644 index 887d66ce..00000000 --- a/testdata/cucumber-expression/matching/matches-short.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: "{short}" -text: '32767' -expected_args: -- 32767 diff --git a/testdata/cucumber-expression/matching/matches-single-quoted-empty-string-as-empty-string-along-with-other-strings.yaml b/testdata/cucumber-expression/matching/matches-single-quoted-empty-string-as-empty-string-along-with-other-strings.yaml deleted file mode 100644 index 877b2d33..00000000 --- a/testdata/cucumber-expression/matching/matches-single-quoted-empty-string-as-empty-string-along-with-other-strings.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -expression: three {string} and {string} mice -text: three '' and 'handsome' mice -expected_args: -- '' -- handsome diff --git a/testdata/cucumber-expression/matching/matches-single-quoted-empty-string-as-empty-string.yaml b/testdata/cucumber-expression/matching/matches-single-quoted-empty-string-as-empty-string.yaml deleted file mode 100644 index 1f3b3f0f..00000000 --- a/testdata/cucumber-expression/matching/matches-single-quoted-empty-string-as-empty-string.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three '' mice -expected_args: -- '' diff --git a/testdata/cucumber-expression/matching/matches-single-quoted-string-with-double-quotes.yaml b/testdata/cucumber-expression/matching/matches-single-quoted-string-with-double-quotes.yaml deleted file mode 100644 index d80a5eae..00000000 --- a/testdata/cucumber-expression/matching/matches-single-quoted-string-with-double-quotes.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three '"blind"' mice -expected_args: -- '"blind"' diff --git a/testdata/cucumber-expression/matching/matches-single-quoted-string-with-escaped-single-quote.yaml b/testdata/cucumber-expression/matching/matches-single-quoted-string-with-escaped-single-quote.yaml deleted file mode 100644 index 253f21de..00000000 --- a/testdata/cucumber-expression/matching/matches-single-quoted-string-with-escaped-single-quote.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three 'bl\'nd' mice -expected_args: -- bl'nd diff --git a/testdata/cucumber-expression/matching/matches-single-quoted-string.yaml b/testdata/cucumber-expression/matching/matches-single-quoted-string.yaml deleted file mode 100644 index 27bd6983..00000000 --- a/testdata/cucumber-expression/matching/matches-single-quoted-string.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {string} mice -text: three 'blind' mice -expected_args: -- blind diff --git a/testdata/cucumber-expression/matching/matches-word.yaml b/testdata/cucumber-expression/matching/matches-word.yaml deleted file mode 100644 index 9a088f93..00000000 --- a/testdata/cucumber-expression/matching/matches-word.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -expression: three {word} mice -text: three blind mice -expected_args: -- blind diff --git a/testdata/cucumber-expression/matching/throws-unknown-parameter-type.yaml b/testdata/cucumber-expression/matching/throws-unknown-parameter-type.yaml deleted file mode 100644 index 3fe10316..00000000 --- a/testdata/cucumber-expression/matching/throws-unknown-parameter-type.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -expression: "{unknown}" -text: something -exception: |- - This Cucumber Expression has a problem at column 1: - - {unknown} - ^-------^ - Undefined parameter type 'unknown'. - Please register a ParameterType for 'unknown' diff --git a/testdata/cucumber-expression/parser/alternation-followed-by-optional.yaml b/testdata/cucumber-expression/parser/alternation-followed-by-optional.yaml deleted file mode 100644 index 8d157fb7..00000000 --- a/testdata/cucumber-expression/parser/alternation-followed-by-optional.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -expression: three blind\ rat/cat(s) -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 23 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: ALTERNATION_NODE - start: 6 - end: 23 - nodes: - - type: ALTERNATIVE_NODE - start: 6 - end: 16 - nodes: - - type: TEXT_NODE - start: 6 - end: 16 - token: blind rat - - type: ALTERNATIVE_NODE - start: 17 - end: 23 - nodes: - - type: TEXT_NODE - start: 17 - end: 20 - token: cat - - type: OPTIONAL_NODE - start: 20 - end: 23 - nodes: - - type: TEXT_NODE - start: 21 - end: 22 - token: s diff --git a/testdata/cucumber-expression/parser/alternation-phrase.yaml b/testdata/cucumber-expression/parser/alternation-phrase.yaml deleted file mode 100644 index 6464bc10..00000000 --- a/testdata/cucumber-expression/parser/alternation-phrase.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -expression: three hungry/blind mice -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 23 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: ALTERNATION_NODE - start: 6 - end: 18 - nodes: - - type: ALTERNATIVE_NODE - start: 6 - end: 12 - nodes: - - type: TEXT_NODE - start: 6 - end: 12 - token: hungry - - type: ALTERNATIVE_NODE - start: 13 - end: 18 - nodes: - - type: TEXT_NODE - start: 13 - end: 18 - token: blind - - type: TEXT_NODE - start: 18 - end: 19 - token: " " - - type: TEXT_NODE - start: 19 - end: 23 - token: mice diff --git a/testdata/cucumber-expression/parser/alternation-with-parameter.yaml b/testdata/cucumber-expression/parser/alternation-with-parameter.yaml deleted file mode 100644 index 40343192..00000000 --- a/testdata/cucumber-expression/parser/alternation-with-parameter.yaml +++ /dev/null @@ -1,75 +0,0 @@ ---- -expression: I select the {int}st/nd/rd/th -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 29 - nodes: - - type: TEXT_NODE - start: 0 - end: 1 - token: I - - type: TEXT_NODE - start: 1 - end: 2 - token: " " - - type: TEXT_NODE - start: 2 - end: 8 - token: select - - type: TEXT_NODE - start: 8 - end: 9 - token: " " - - type: TEXT_NODE - start: 9 - end: 12 - token: the - - type: TEXT_NODE - start: 12 - end: 13 - token: " " - - type: PARAMETER_NODE - start: 13 - end: 18 - nodes: - - type: TEXT_NODE - start: 14 - end: 17 - token: int - - type: ALTERNATION_NODE - start: 18 - end: 29 - nodes: - - type: ALTERNATIVE_NODE - start: 18 - end: 20 - nodes: - - type: TEXT_NODE - start: 18 - end: 20 - token: st - - type: ALTERNATIVE_NODE - start: 21 - end: 23 - nodes: - - type: TEXT_NODE - start: 21 - end: 23 - token: nd - - type: ALTERNATIVE_NODE - start: 24 - end: 26 - nodes: - - type: TEXT_NODE - start: 24 - end: 26 - token: rd - - type: ALTERNATIVE_NODE - start: 27 - end: 29 - nodes: - - type: TEXT_NODE - start: 27 - end: 29 - token: th diff --git a/testdata/cucumber-expression/parser/alternation-with-unused-end-optional.yaml b/testdata/cucumber-expression/parser/alternation-with-unused-end-optional.yaml deleted file mode 100644 index beedb309..00000000 --- a/testdata/cucumber-expression/parser/alternation-with-unused-end-optional.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -expression: three )blind\ mice/rats -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 23 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: ALTERNATION_NODE - start: 6 - end: 23 - nodes: - - type: ALTERNATIVE_NODE - start: 6 - end: 18 - nodes: - - type: TEXT_NODE - start: 6 - end: 7 - token: ")" - - type: TEXT_NODE - start: 7 - end: 18 - token: blind mice - - type: ALTERNATIVE_NODE - start: 19 - end: 23 - nodes: - - type: TEXT_NODE - start: 19 - end: 23 - token: rats diff --git a/testdata/cucumber-expression/parser/alternation-with-unused-start-optional.yaml b/testdata/cucumber-expression/parser/alternation-with-unused-start-optional.yaml deleted file mode 100644 index 1bdf95fd..00000000 --- a/testdata/cucumber-expression/parser/alternation-with-unused-start-optional.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: three blind\ mice/rats( -exception: |- - This Cucumber Expression has a problem at column 23: - - three blind\ mice/rats( - ^ - The '(' does not have a matching ')'. - If you did not intend to use optional text you can use '\(' to escape the optional text diff --git a/testdata/cucumber-expression/parser/alternation-with-white-space.yaml b/testdata/cucumber-expression/parser/alternation-with-white-space.yaml deleted file mode 100644 index d96c2d6d..00000000 --- a/testdata/cucumber-expression/parser/alternation-with-white-space.yaml +++ /dev/null @@ -1,27 +0,0 @@ ---- -expression: "\\ three\\ hungry/blind\\ mice\\ " -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 29 - nodes: - - type: ALTERNATION_NODE - start: 0 - end: 29 - nodes: - - type: ALTERNATIVE_NODE - start: 0 - end: 15 - nodes: - - type: TEXT_NODE - start: 0 - end: 15 - token: " three hungry" - - type: ALTERNATIVE_NODE - start: 16 - end: 29 - nodes: - - type: TEXT_NODE - start: 16 - end: 29 - token: 'blind mice ' diff --git a/testdata/cucumber-expression/parser/alternation.yaml b/testdata/cucumber-expression/parser/alternation.yaml deleted file mode 100644 index b21db65b..00000000 --- a/testdata/cucumber-expression/parser/alternation.yaml +++ /dev/null @@ -1,27 +0,0 @@ ---- -expression: mice/rats -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 9 - nodes: - - type: ALTERNATION_NODE - start: 0 - end: 9 - nodes: - - type: ALTERNATIVE_NODE - start: 0 - end: 4 - nodes: - - type: TEXT_NODE - start: 0 - end: 4 - token: mice - - type: ALTERNATIVE_NODE - start: 5 - end: 9 - nodes: - - type: TEXT_NODE - start: 5 - end: 9 - token: rats diff --git a/testdata/cucumber-expression/parser/anonymous-parameter.yaml b/testdata/cucumber-expression/parser/anonymous-parameter.yaml deleted file mode 100644 index 861f68b7..00000000 --- a/testdata/cucumber-expression/parser/anonymous-parameter.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: "{}" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 2 - nodes: - - type: PARAMETER_NODE - start: 0 - end: 2 - nodes: [] diff --git a/testdata/cucumber-expression/parser/closing-brace.yaml b/testdata/cucumber-expression/parser/closing-brace.yaml deleted file mode 100644 index bcda9d13..00000000 --- a/testdata/cucumber-expression/parser/closing-brace.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: "}" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 1 - nodes: - - type: TEXT_NODE - start: 0 - end: 1 - token: "}" diff --git a/testdata/cucumber-expression/parser/closing-parenthesis.yaml b/testdata/cucumber-expression/parser/closing-parenthesis.yaml deleted file mode 100644 index 5ece5382..00000000 --- a/testdata/cucumber-expression/parser/closing-parenthesis.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: ")" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 1 - nodes: - - type: TEXT_NODE - start: 0 - end: 1 - token: ")" diff --git a/testdata/cucumber-expression/parser/empty-alternation.yaml b/testdata/cucumber-expression/parser/empty-alternation.yaml deleted file mode 100644 index ba724c8a..00000000 --- a/testdata/cucumber-expression/parser/empty-alternation.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -expression: "/" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 1 - nodes: - - type: ALTERNATION_NODE - start: 0 - end: 1 - nodes: - - type: ALTERNATIVE_NODE - start: 0 - end: 0 - nodes: [] - - type: ALTERNATIVE_NODE - start: 1 - end: 1 - nodes: [] diff --git a/testdata/cucumber-expression/parser/empty-alternations.yaml b/testdata/cucumber-expression/parser/empty-alternations.yaml deleted file mode 100644 index 47e874c6..00000000 --- a/testdata/cucumber-expression/parser/empty-alternations.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -expression: "//" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 2 - nodes: - - type: ALTERNATION_NODE - start: 0 - end: 2 - nodes: - - type: ALTERNATIVE_NODE - start: 0 - end: 0 - nodes: [] - - type: ALTERNATIVE_NODE - start: 1 - end: 1 - nodes: [] - - type: ALTERNATIVE_NODE - start: 2 - end: 2 - nodes: [] diff --git a/testdata/cucumber-expression/parser/empty-string.yaml b/testdata/cucumber-expression/parser/empty-string.yaml deleted file mode 100644 index 5a3b3b82..00000000 --- a/testdata/cucumber-expression/parser/empty-string.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -expression: '' -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 0 - nodes: [] diff --git a/testdata/cucumber-expression/parser/escaped-alternation.yaml b/testdata/cucumber-expression/parser/escaped-alternation.yaml deleted file mode 100644 index 86650014..00000000 --- a/testdata/cucumber-expression/parser/escaped-alternation.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: mice\/rats -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 10 - nodes: - - type: TEXT_NODE - start: 0 - end: 10 - token: mice/rats diff --git a/testdata/cucumber-expression/parser/escaped-backslash.yaml b/testdata/cucumber-expression/parser/escaped-backslash.yaml deleted file mode 100644 index 30e8bcfa..00000000 --- a/testdata/cucumber-expression/parser/escaped-backslash.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: "\\\\" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 2 - nodes: - - type: TEXT_NODE - start: 0 - end: 2 - token: "\\" diff --git a/testdata/cucumber-expression/parser/escaped-opening-parenthesis.yaml b/testdata/cucumber-expression/parser/escaped-opening-parenthesis.yaml deleted file mode 100644 index 5933e377..00000000 --- a/testdata/cucumber-expression/parser/escaped-opening-parenthesis.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: "\\(" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 2 - nodes: - - type: TEXT_NODE - start: 0 - end: 2 - token: "(" diff --git a/testdata/cucumber-expression/parser/escaped-optional-followed-by-optional.yaml b/testdata/cucumber-expression/parser/escaped-optional-followed-by-optional.yaml deleted file mode 100644 index 206f5383..00000000 --- a/testdata/cucumber-expression/parser/escaped-optional-followed-by-optional.yaml +++ /dev/null @@ -1,47 +0,0 @@ ---- -expression: three \((very) blind) mice -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 26 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: TEXT_NODE - start: 6 - end: 8 - token: "(" - - type: OPTIONAL_NODE - start: 8 - end: 14 - nodes: - - type: TEXT_NODE - start: 9 - end: 13 - token: very - - type: TEXT_NODE - start: 14 - end: 15 - token: " " - - type: TEXT_NODE - start: 15 - end: 20 - token: blind - - type: TEXT_NODE - start: 20 - end: 21 - token: ")" - - type: TEXT_NODE - start: 21 - end: 22 - token: " " - - type: TEXT_NODE - start: 22 - end: 26 - token: mice diff --git a/testdata/cucumber-expression/parser/escaped-optional-phrase.yaml b/testdata/cucumber-expression/parser/escaped-optional-phrase.yaml deleted file mode 100644 index 6b1c7430..00000000 --- a/testdata/cucumber-expression/parser/escaped-optional-phrase.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -expression: three \(blind) mice -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 19 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: TEXT_NODE - start: 6 - end: 13 - token: "(blind" - - type: TEXT_NODE - start: 13 - end: 14 - token: ")" - - type: TEXT_NODE - start: 14 - end: 15 - token: " " - - type: TEXT_NODE - start: 15 - end: 19 - token: mice diff --git a/testdata/cucumber-expression/parser/escaped-optional.yaml b/testdata/cucumber-expression/parser/escaped-optional.yaml deleted file mode 100644 index 0ade829b..00000000 --- a/testdata/cucumber-expression/parser/escaped-optional.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -expression: "\\(blind)" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 8 - nodes: - - type: TEXT_NODE - start: 0 - end: 7 - token: "(blind" - - type: TEXT_NODE - start: 7 - end: 8 - token: ")" diff --git a/testdata/cucumber-expression/parser/opening-brace.yaml b/testdata/cucumber-expression/parser/opening-brace.yaml deleted file mode 100644 index 60d8adcf..00000000 --- a/testdata/cucumber-expression/parser/opening-brace.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: "{" -exception: |- - This Cucumber Expression has a problem at column 1: - - { - ^ - The '{' does not have a matching '}'. - If you did not intend to use a parameter you can use '\{' to escape the a parameter diff --git a/testdata/cucumber-expression/parser/opening-parenthesis.yaml b/testdata/cucumber-expression/parser/opening-parenthesis.yaml deleted file mode 100644 index 8ef846cf..00000000 --- a/testdata/cucumber-expression/parser/opening-parenthesis.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: "(" -exception: |- - This Cucumber Expression has a problem at column 1: - - ( - ^ - The '(' does not have a matching ')'. - If you did not intend to use optional text you can use '\(' to escape the optional text diff --git a/testdata/cucumber-expression/parser/optional-containing-nested-optional.yaml b/testdata/cucumber-expression/parser/optional-containing-nested-optional.yaml deleted file mode 100644 index eae10379..00000000 --- a/testdata/cucumber-expression/parser/optional-containing-nested-optional.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -expression: three ((very) blind) mice -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 25 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: OPTIONAL_NODE - start: 6 - end: 20 - nodes: - - type: OPTIONAL_NODE - start: 7 - end: 13 - nodes: - - type: TEXT_NODE - start: 8 - end: 12 - token: very - - type: TEXT_NODE - start: 13 - end: 14 - token: " " - - type: TEXT_NODE - start: 14 - end: 19 - token: blind - - type: TEXT_NODE - start: 20 - end: 21 - token: " " - - type: TEXT_NODE - start: 21 - end: 25 - token: mice diff --git a/testdata/cucumber-expression/parser/optional-phrase.yaml b/testdata/cucumber-expression/parser/optional-phrase.yaml deleted file mode 100644 index 71ae2107..00000000 --- a/testdata/cucumber-expression/parser/optional-phrase.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -expression: three (blind) mice -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 18 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: OPTIONAL_NODE - start: 6 - end: 13 - nodes: - - type: TEXT_NODE - start: 7 - end: 12 - token: blind - - type: TEXT_NODE - start: 13 - end: 14 - token: " " - - type: TEXT_NODE - start: 14 - end: 18 - token: mice diff --git a/testdata/cucumber-expression/parser/optional.yaml b/testdata/cucumber-expression/parser/optional.yaml deleted file mode 100644 index 5fafe695..00000000 --- a/testdata/cucumber-expression/parser/optional.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -expression: "(blind)" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 7 - nodes: - - type: OPTIONAL_NODE - start: 0 - end: 7 - nodes: - - type: TEXT_NODE - start: 1 - end: 6 - token: blind diff --git a/testdata/cucumber-expression/parser/parameter.yaml b/testdata/cucumber-expression/parser/parameter.yaml deleted file mode 100644 index 596e86fe..00000000 --- a/testdata/cucumber-expression/parser/parameter.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -expression: "{string}" -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 8 - nodes: - - type: PARAMETER_NODE - start: 0 - end: 8 - nodes: - - type: TEXT_NODE - start: 1 - end: 7 - token: string diff --git a/testdata/cucumber-expression/parser/phrase.yaml b/testdata/cucumber-expression/parser/phrase.yaml deleted file mode 100644 index 09704867..00000000 --- a/testdata/cucumber-expression/parser/phrase.yaml +++ /dev/null @@ -1,27 +0,0 @@ ---- -expression: three blind mice -expected_ast: - type: EXPRESSION_NODE - start: 0 - end: 16 - nodes: - - type: TEXT_NODE - start: 0 - end: 5 - token: three - - type: TEXT_NODE - start: 5 - end: 6 - token: " " - - type: TEXT_NODE - start: 6 - end: 11 - token: blind - - type: TEXT_NODE - start: 11 - end: 12 - token: " " - - type: TEXT_NODE - start: 12 - end: 16 - token: mice diff --git a/testdata/cucumber-expression/parser/unfinished-parameter.yaml b/testdata/cucumber-expression/parser/unfinished-parameter.yaml deleted file mode 100644 index 9ecc2889..00000000 --- a/testdata/cucumber-expression/parser/unfinished-parameter.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: "{string" -exception: |- - This Cucumber Expression has a problem at column 1: - - {string - ^ - The '{' does not have a matching '}'. - If you did not intend to use a parameter you can use '\{' to escape the a parameter diff --git a/testdata/cucumber-expression/tokenizer/alternation-phrase.yaml b/testdata/cucumber-expression/tokenizer/alternation-phrase.yaml deleted file mode 100644 index 1f218196..00000000 --- a/testdata/cucumber-expression/tokenizer/alternation-phrase.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -expression: three blind/cripple mice -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 5 - text: three -- type: WHITE_SPACE - start: 5 - end: 6 - text: " " -- type: TEXT - start: 6 - end: 11 - text: blind -- type: ALTERNATION - start: 11 - end: 12 - text: "/" -- type: TEXT - start: 12 - end: 19 - text: cripple -- type: WHITE_SPACE - start: 19 - end: 20 - text: " " -- type: TEXT - start: 20 - end: 24 - text: mice -- type: END_OF_LINE - start: 24 - end: 24 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/alternation.yaml b/testdata/cucumber-expression/tokenizer/alternation.yaml deleted file mode 100644 index e53eb4b1..00000000 --- a/testdata/cucumber-expression/tokenizer/alternation.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -expression: blind/cripple -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 5 - text: blind -- type: ALTERNATION - start: 5 - end: 6 - text: "/" -- type: TEXT - start: 6 - end: 13 - text: cripple -- type: END_OF_LINE - start: 13 - end: 13 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/empty-string.yaml b/testdata/cucumber-expression/tokenizer/empty-string.yaml deleted file mode 100644 index b4ca67c4..00000000 --- a/testdata/cucumber-expression/tokenizer/empty-string.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -expression: '' -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: END_OF_LINE - start: 0 - end: 0 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/escape-non-reserved-character.yaml b/testdata/cucumber-expression/tokenizer/escape-non-reserved-character.yaml deleted file mode 100644 index 98c5bd5c..00000000 --- a/testdata/cucumber-expression/tokenizer/escape-non-reserved-character.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: "\\[" -exception: |- - This Cucumber Expression has a problem at column 2: - - \[ - ^ - Only the characters '{', '}', '(', ')', '\', '/' and whitespace can be escaped. - If you did mean to use an '\' you can use '\\' to escape it diff --git a/testdata/cucumber-expression/tokenizer/escaped-alternation.yaml b/testdata/cucumber-expression/tokenizer/escaped-alternation.yaml deleted file mode 100644 index 57f4106d..00000000 --- a/testdata/cucumber-expression/tokenizer/escaped-alternation.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -expression: blind\ and\ famished\/cripple mice -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 29 - text: blind and famished/cripple -- type: WHITE_SPACE - start: 29 - end: 30 - text: " " -- type: TEXT - start: 30 - end: 34 - text: mice -- type: END_OF_LINE - start: 34 - end: 34 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/escaped-char-has-start-index-of-text-token.yaml b/testdata/cucumber-expression/tokenizer/escaped-char-has-start-index-of-text-token.yaml deleted file mode 100644 index 9fe90a7a..00000000 --- a/testdata/cucumber-expression/tokenizer/escaped-char-has-start-index-of-text-token.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -expression: " \\/ " -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: WHITE_SPACE - start: 0 - end: 1 - text: " " -- type: TEXT - start: 1 - end: 3 - text: "/" -- type: WHITE_SPACE - start: 3 - end: 4 - text: " " -- type: END_OF_LINE - start: 4 - end: 4 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/escaped-end-of-line.yaml b/testdata/cucumber-expression/tokenizer/escaped-end-of-line.yaml deleted file mode 100644 index c5a9e022..00000000 --- a/testdata/cucumber-expression/tokenizer/escaped-end-of-line.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -expression: "\\" -exception: |- - This Cucumber Expression has a problem at column 1: - - \ - ^ - The end of line can not be escaped. - You can use '\\' to escape the '\' diff --git a/testdata/cucumber-expression/tokenizer/escaped-optional.yaml b/testdata/cucumber-expression/tokenizer/escaped-optional.yaml deleted file mode 100644 index e4436f09..00000000 --- a/testdata/cucumber-expression/tokenizer/escaped-optional.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -expression: "\\(blind\\)" -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 9 - text: "(blind)" -- type: END_OF_LINE - start: 9 - end: 9 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/escaped-parameter.yaml b/testdata/cucumber-expression/tokenizer/escaped-parameter.yaml deleted file mode 100644 index 7c0ec0d4..00000000 --- a/testdata/cucumber-expression/tokenizer/escaped-parameter.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -expression: "\\{string\\}" -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 10 - text: "{string}" -- type: END_OF_LINE - start: 10 - end: 10 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/escaped-space.yaml b/testdata/cucumber-expression/tokenizer/escaped-space.yaml deleted file mode 100644 index 77ac277b..00000000 --- a/testdata/cucumber-expression/tokenizer/escaped-space.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -expression: "\\ " -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 2 - text: " " -- type: END_OF_LINE - start: 2 - end: 2 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/optional-phrase.yaml b/testdata/cucumber-expression/tokenizer/optional-phrase.yaml deleted file mode 100644 index f0572d7b..00000000 --- a/testdata/cucumber-expression/tokenizer/optional-phrase.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -expression: three (blind) mice -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 5 - text: three -- type: WHITE_SPACE - start: 5 - end: 6 - text: " " -- type: BEGIN_OPTIONAL - start: 6 - end: 7 - text: "(" -- type: TEXT - start: 7 - end: 12 - text: blind -- type: END_OPTIONAL - start: 12 - end: 13 - text: ")" -- type: WHITE_SPACE - start: 13 - end: 14 - text: " " -- type: TEXT - start: 14 - end: 18 - text: mice -- type: END_OF_LINE - start: 18 - end: 18 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/optional.yaml b/testdata/cucumber-expression/tokenizer/optional.yaml deleted file mode 100644 index 744ef692..00000000 --- a/testdata/cucumber-expression/tokenizer/optional.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -expression: "(blind)" -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: BEGIN_OPTIONAL - start: 0 - end: 1 - text: "(" -- type: TEXT - start: 1 - end: 6 - text: blind -- type: END_OPTIONAL - start: 6 - end: 7 - text: ")" -- type: END_OF_LINE - start: 7 - end: 7 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/parameter-phrase.yaml b/testdata/cucumber-expression/tokenizer/parameter-phrase.yaml deleted file mode 100644 index 3738510e..00000000 --- a/testdata/cucumber-expression/tokenizer/parameter-phrase.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -expression: three {string} mice -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 5 - text: three -- type: WHITE_SPACE - start: 5 - end: 6 - text: " " -- type: BEGIN_PARAMETER - start: 6 - end: 7 - text: "{" -- type: TEXT - start: 7 - end: 13 - text: string -- type: END_PARAMETER - start: 13 - end: 14 - text: "}" -- type: WHITE_SPACE - start: 14 - end: 15 - text: " " -- type: TEXT - start: 15 - end: 19 - text: mice -- type: END_OF_LINE - start: 19 - end: 19 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/parameter.yaml b/testdata/cucumber-expression/tokenizer/parameter.yaml deleted file mode 100644 index 763e47cb..00000000 --- a/testdata/cucumber-expression/tokenizer/parameter.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -expression: "{string}" -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: BEGIN_PARAMETER - start: 0 - end: 1 - text: "{" -- type: TEXT - start: 1 - end: 7 - text: string -- type: END_PARAMETER - start: 7 - end: 8 - text: "}" -- type: END_OF_LINE - start: 8 - end: 8 - text: '' diff --git a/testdata/cucumber-expression/tokenizer/phrase.yaml b/testdata/cucumber-expression/tokenizer/phrase.yaml deleted file mode 100644 index d6551356..00000000 --- a/testdata/cucumber-expression/tokenizer/phrase.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -expression: three blind mice -expected_tokens: -- type: START_OF_LINE - start: 0 - end: 0 - text: '' -- type: TEXT - start: 0 - end: 5 - text: three -- type: WHITE_SPACE - start: 5 - end: 6 - text: " " -- type: TEXT - start: 6 - end: 11 - text: blind -- type: WHITE_SPACE - start: 11 - end: 12 - text: " " -- type: TEXT - start: 12 - end: 16 - text: mice -- type: END_OF_LINE - start: 16 - end: 16 - text: '' diff --git a/testdata/cucumber-expression/transformation/alternation-with-optional.yaml b/testdata/cucumber-expression/transformation/alternation-with-optional.yaml deleted file mode 100644 index 159b7293..00000000 --- a/testdata/cucumber-expression/transformation/alternation-with-optional.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: a/b(c) -expected_regex: "^(?:a|b(?:c)?)$" diff --git a/testdata/cucumber-expression/transformation/alternation.yaml b/testdata/cucumber-expression/transformation/alternation.yaml deleted file mode 100644 index f49593b3..00000000 --- a/testdata/cucumber-expression/transformation/alternation.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: a/b c/d/e -expected_regex: "^(?:a|b) (?:c|d|e)$" diff --git a/testdata/cucumber-expression/transformation/empty.yaml b/testdata/cucumber-expression/transformation/empty.yaml deleted file mode 100644 index 2fec72c2..00000000 --- a/testdata/cucumber-expression/transformation/empty.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: '' -expected_regex: "^$" diff --git a/testdata/cucumber-expression/transformation/escape-regex-characters.yaml b/testdata/cucumber-expression/transformation/escape-regex-characters.yaml deleted file mode 100644 index f13c5a7c..00000000 --- a/testdata/cucumber-expression/transformation/escape-regex-characters.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: "^$[]\\(\\){}\\\\.|?*+" -expected_regex: "^\\^\\$\\[\\]\\(\\)(.*)\\\\\\.\\|\\?\\*\\+$" diff --git a/testdata/cucumber-expression/transformation/optional.yaml b/testdata/cucumber-expression/transformation/optional.yaml deleted file mode 100644 index ec3eb087..00000000 --- a/testdata/cucumber-expression/transformation/optional.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: "(a)" -expected_regex: "^(?:a)?$" diff --git a/testdata/cucumber-expression/transformation/parameter.yaml b/testdata/cucumber-expression/transformation/parameter.yaml deleted file mode 100644 index 962eda14..00000000 --- a/testdata/cucumber-expression/transformation/parameter.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: "{int}" -expected_regex: "^((?:-?\\d+)|(?:\\d+))$" diff --git a/testdata/cucumber-expression/transformation/text.yaml b/testdata/cucumber-expression/transformation/text.yaml deleted file mode 100644 index 8839c1a7..00000000 --- a/testdata/cucumber-expression/transformation/text.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: a -expected_regex: "^a$" diff --git a/testdata/cucumber-expression/transformation/unicode.yaml b/testdata/cucumber-expression/transformation/unicode.yaml deleted file mode 100644 index b2b985c3..00000000 --- a/testdata/cucumber-expression/transformation/unicode.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expression: Привет, Мир(ы)! -expected_regex: "^Привет, Мир(?:ы)?!$" diff --git a/testdata/regular-expression/matching/optional-capture-groups-all.yaml b/testdata/regular-expression/matching/optional-capture-groups-all.yaml deleted file mode 100644 index 61e78566..00000000 --- a/testdata/regular-expression/matching/optional-capture-groups-all.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -expression: "^a (b )?c (d )?e (f )?g$" -text: a b c d e f g -expected_args: -- 'b ' -- 'd ' -- 'f ' diff --git a/testdata/regular-expression/matching/optional-capture-groups-issue.yaml b/testdata/regular-expression/matching/optional-capture-groups-issue.yaml deleted file mode 100644 index 50e8d62b..00000000 --- a/testdata/regular-expression/matching/optional-capture-groups-issue.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -expression: "^I should( not)? be on the map$" -text: I should be on the map -expected_args: [null] diff --git a/testdata/regular-expression/matching/optional-capture-groups-some.yaml b/testdata/regular-expression/matching/optional-capture-groups-some.yaml deleted file mode 100644 index 7e597cf1..00000000 --- a/testdata/regular-expression/matching/optional-capture-groups-some.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -expression: "^a (b )?c (d )?e (f )?g$" -text: a b c e f g -expected_args: -- 'b ' -- -- 'f ' diff --git a/testdata/tag-expression/evaluations.yml b/testdata/tag-expression/evaluations.yml deleted file mode 100644 index 2ee4e075..00000000 --- a/testdata/tag-expression/evaluations.yml +++ /dev/null @@ -1,59 +0,0 @@ ---- -- expression: 'not x' - tests: - - variables: ['x'] - result: false - - variables: ['y'] - result: true -- expression: 'x and y' - tests: - - variables: ['x', 'y'] - result: true - - variables: ['x'] - result: false - - variables: ['y'] - result: false -- expression: 'x or y' - tests: - - variables: [] - result: false - - variables: ['x', 'y'] - result: true - - variables: ['x'] - result: true - - variables: ['y'] - result: true -- expression: 'x\(1\) or y\(2\)' - tests: - - variables: ['x(1)'] - result: true - - variables: ['y(2)'] - result: true -- expression: 'x\\ or y\\\) or z\\' - tests: - - variables: ['x\'] - result: true - - variables: ['y\)'] - result: true - - variables: ['z\'] - result: true - - variables: ['x'] - result: false - - variables: ['y)'] - result: false - - variables: ['z'] - result: false -- expression: '\\x or y\\ or z\\' - tests: - - variables: ['\x'] - result: true - - variables: ['y\'] - result: true - - variables: ['z\'] - result: true - - variables: ['x'] - result: false - - variables: ['y'] - result: false - - variables: ['z'] - result: false diff --git a/testdata/tag-expression/parsing.yml b/testdata/tag-expression/parsing.yml deleted file mode 100644 index b5bb560d..00000000 --- a/testdata/tag-expression/parsing.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -- expression: '' - formatted: 'true' -- expression: 'a and b' - formatted: '( a and b )' -- expression: 'a or b' - formatted: '( a or b )' -- expression: 'not a' - formatted: 'not ( a )' -- expression: 'a and b and c' - formatted: '( ( a and b ) and c )' -- expression: '( a and b ) or ( c and d )' - formatted: '( ( a and b ) or ( c and d ) )' -- expression: 'not a or b and not c or not d or e and f' - formatted: '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )' -- expression: 'not a\(\) or b and not c or not d or e and f' - formatted: '( ( ( not ( a\(\) ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )' -- expression: 'not (a and b)' - formatted: 'not ( a and b )' -- expression: 'not (a or b)' - formatted: 'not ( a or b )' -- expression: 'not (a and b) and c or not (d or f)' - formatted: '( ( not ( a and b ) and c ) or not ( d or f ) )' -- expression: 'a\\ and b' - formatted: '( a\\ and b )' -- expression: '\\a and b' - formatted: '( \\a and b )' -- expression: 'a\\ and b' - formatted: '( a\\ and b )' -- expression: 'a and b\\' - formatted: '( a and b\\ )' -- expression: '( a and b\\\\)' - formatted: '( a and b\\\\ )' -- expression: 'a\\\( and b\\\)' - formatted: '( a\\\( and b\\\) )' -- expression: '(a and \\b)' - formatted: '( a and \\b )' -- expression: 'x or(y) ' - formatted: '( x or y )' -- expression: 'x\(1\) or(y\(2\))' - formatted: '( x\(1\) or y\(2\) )' -- expression: '\\x or y\\ or z\\' - formatted: '( ( \\x or y\\ ) or z\\ )' -- expression: 'x\\ or(y\\\)) or(z\\)' - formatted: '( ( x\\ or y\\\) ) or z\\ )' -- expression: 'x\ or y' - formatted: '( x\ or y )'