[
  {
    "path": ".editorconfig",
    "content": "[*]\nindent_size = 4\n\n[{*.kt,*.kts}]\nij_kotlin_code_style_defaults = KOTLIN_OFFICIAL\nij_kotlin_continuation_indent_size = 4"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: olivernybroe"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nPlease report a bug to [YouTrack](https://youtrack.jetbrains.com/newIssue?project=WI)\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nCreate a discussion instead.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- Description of what has been changed or added in this pr and why -->\n\n- [ ] Added or updated tests\n- [ ] Updated `CHANGELOG.md`\n\nissues: #...\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# Dependabot configuration:\n# https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gradle\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/auto-close.yml",
    "content": "on:\n  issues:\n    types: [opened, edited]\n\njobs:\n  auto_close_issues:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v1\n      - name: Automatically close issues that don't follow the issue template\n        uses: lucasbento/auto-close-issues@v1.0.2\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          issue-close-message: \"@${issue.user.login}: hello! :wave:\\n\\nThis issue is being automatically closed because it does not follow the issue template.\" # optional property\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "# GitHub Actions Workflow created for testing and preparing the plugin release in following steps:\n# - validate Gradle Wrapper,\n# - run 'test' and 'verifyPlugin' tasks,\n# - run Qodana inspections,\n# - run 'buildPlugin' task and prepare artifact for the further tests,\n# - run 'runPluginVerifier' task,\n# - create a draft release.\n#\n# Workflow is triggered on push and pull_request events.\n#\n# GitHub Actions reference: https://help.github.com/en/actions\n#\n## JBIJPPTPL\n\nname: Build\non:\n  # Trigger the workflow on pushes to only the 'main' branch (this avoids duplicate checks being run e.g. for dependabot pull requests)\n  push:\n    branches: [main]\n  # Trigger the workflow on any pull request\n  pull_request:\n\njobs:\n\n  # Run Gradle Wrapper Validation Action to verify the wrapper's checksum\n  # Run verifyPlugin, IntelliJ Plugin Verifier, and test Gradle tasks\n  # Build plugin and provide the artifact for the next workflow jobs\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    outputs:\n      version: ${{ steps.properties.outputs.version }}\n      changelog: ${{ steps.properties.outputs.changelog }}\n    steps:\n\n      # Check out current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v2.4.0\n\n      # Validate wrapper\n      - name: Gradle Wrapper Validation\n        uses: gradle/wrapper-validation-action@v1.0.4\n\n      # Setup Java 11 environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v2\n        with:\n          distribution: zulu\n          java-version: 11\n          cache: gradle\n\n      # Set environment variables\n      - name: Export Properties\n        id: properties\n        shell: bash\n        run: |\n          PROPERTIES=\"$(./gradlew properties --console=plain -q)\"\n          VERSION=\"$(echo \"$PROPERTIES\" | grep \"^version:\" | cut -f2- -d ' ')\"\n          NAME=\"$(echo \"$PROPERTIES\" | grep \"^pluginName:\" | cut -f2- -d ' ')\"\n          CHANGELOG=\"$(./gradlew getChangelog --unreleased --no-header --console=plain -q)\"\n          CHANGELOG=\"${CHANGELOG//'%'/'%25'}\"\n          CHANGELOG=\"${CHANGELOG//$'\\n'/'%0A'}\"\n          CHANGELOG=\"${CHANGELOG//$'\\r'/'%0D'}\"\n\n          echo \"::set-output name=version::$VERSION\"\n          echo \"::set-output name=name::$NAME\"\n          echo \"::set-output name=changelog::$CHANGELOG\"\n          echo \"::set-output name=pluginVerifierHomeDir::~/.pluginVerifier\"\n\n          ./gradlew listProductsReleases # prepare list of IDEs for Plugin Verifier\n\n      # Run tests\n      - name: Run Tests\n        run: ./gradlew test\n\n      # Collect Tests Result of failed tests\n      - name: Collect Tests Result\n        if: ${{ failure() }}\n        uses: actions/upload-artifact@v2\n        with:\n          name: tests-result\n          path: ${{ github.workspace }}/build/reports/tests\n\n#      # Cache Plugin Verifier IDEs\n#      - name: Setup Plugin Verifier IDEs Cache\n#        uses: actions/cache@v2.1.7\n#        with:\n#          path: ${{ steps.properties.outputs.pluginVerifierHomeDir }}/ides\n#          key: plugin-verifier-${{ hashFiles('build/listProductsReleases.txt') }}\n#\n#      # Run Verify Plugin task and IntelliJ Plugin Verifier tool\n#      - name: Run Plugin Verification tasks\n#        run: ./gradlew runPluginVerifier -Pplugin.verifier.home.dir=${{ steps.properties.outputs.pluginVerifierHomeDir }}\n#\n#      # Collect Plugin Verifier Result\n#      - name: Collect Plugin Verifier Result\n#        if: ${{ always() }}\n#        uses: actions/upload-artifact@v2\n#        with:\n#          name: pluginVerifier-result\n#          path: ${{ github.workspace }}/build/reports/pluginVerifier\n\n      # TODO: temp needed because verifier disabled\n      - run: ./gradlew buildPlugin\n\n      # Prepare plugin archive content for creating artifact\n      - name: Prepare Plugin Artifact\n        id: artifact\n        shell: bash\n        run: |\n          cd ${{ github.workspace }}/build/distributions\n          FILENAME=`ls *.zip`\n          unzip \"$FILENAME\" -d content\n\n          echo \"::set-output name=filename::${FILENAME:0:-4}\"\n\n      # Store already-built plugin as an artifact for downloading\n      - name: Upload artifact\n        uses: actions/upload-artifact@v2.2.4\n        with:\n          name: ${{ steps.artifact.outputs.filename }}\n          path: ./build/distributions/content/*/*\n\n  # Prepare a draft release for GitHub Releases page for the manual verification\n  # If accepted and published, release workflow would be triggered\n  releaseDraft:\n    name: Release Draft\n    if: github.event_name != 'pull_request'\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n\n      # Check out current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v2.4.0\n\n      # Remove old release drafts by using the curl request for the available releases with draft flag\n      - name: Remove Old Release Drafts\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          gh api repos/{owner}/{repo}/releases \\\n            --jq '.[] | select(.draft == true) | .id' \\\n            | xargs -I '{}' gh api -X DELETE repos/{owner}/{repo}/releases/{}\n\n      # Create new release draft - which is not publicly visible and requires manual acceptance\n      - name: Create Release Draft\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          gh release create v${{ needs.build.outputs.version }} \\\n            --draft \\\n            --title \"v${{ needs.build.outputs.version }}\" \\\n            --notes \"$(cat << 'EOM'\n          ${{ needs.build.outputs.changelog }}\n          EOM\n          )\""
  },
  {
    "path": ".github/workflows/qodana.yml",
    "content": "name: Qodana\non:\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - main\n\njobs:\n  qodana:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      # Setup Java 11 environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v2\n        with:\n          distribution: zulu\n          java-version: 11\n          cache: gradle\n\n      # Build\n      - name: Run Build\n        run: ./gradlew build\n\n      - name: 'Qodana Scan'\n        uses: JetBrains/qodana-action@v2023.1.0\n        env:\n          QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "# GitHub Actions Workflow created for handling the release process based on the draft release prepared\n# with the Build workflow. Running the publishPlugin task requires the PUBLISH_TOKEN secret provided.\n\nname: Release\non:\n  release:\n    types: [prereleased, released]\n\njobs:\n\n  # Prepare and publish the plugin to the Marketplace repository\n  release:\n    name: Publish Plugin\n    runs-on: ubuntu-latest\n    steps:\n\n      # Check out current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v2.4.0\n        with:\n          ref: ${{ github.event.release.tag_name }}\n\n      # Setup Java 11 environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v2\n        with:\n          distribution: zulu\n          java-version: 11\n          cache: gradle\n\n      # Set environment variables\n      - name: Export Properties\n        id: properties\n        shell: bash\n        run: |\n          CHANGELOG=\"$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d'\n          ${{ github.event.release.body }}\n          EOM\n          )\"\n          \n          CHANGELOG=\"${CHANGELOG//'%'/'%25'}\"\n          CHANGELOG=\"${CHANGELOG//$'\\n'/'%0A'}\"\n          CHANGELOG=\"${CHANGELOG//$'\\r'/'%0D'}\"\n\n          echo \"::set-output name=changelog::$CHANGELOG\"\n\n      # Update Unreleased section with the current release note\n      - name: Patch Changelog\n        if: ${{ steps.properties.outputs.changelog != '' }}\n        env:\n          CHANGELOG: ${{ steps.properties.outputs.changelog }}\n        run: |\n          ./gradlew patchChangelog --release-note=\"$CHANGELOG\"\n\n      # Publish the plugin to the Marketplace\n      - name: Publish Plugin\n        env:\n          PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}\n        run: ./gradlew publishPlugin\n\n      # Upload artifact as a release asset\n      - name: Upload Release Asset\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/*\n\n      # Create pull request\n      - name: Create Pull Request\n        if: ${{ steps.properties.outputs.changelog != '' }}\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          VERSION=\"${{ github.event.release.tag_name }}\"\n          BRANCH=\"changelog-update-$VERSION\"\n\n          git config user.email \"action@github.com\"\n          git config user.name \"GitHub Action\"\n\n          git checkout -b $BRANCH\n          git commit -am \"Changelog update - $VERSION\"\n          git push --set-upstream origin $BRANCH\n\n          gh pr create \\\n            --title \"Changelog update - \\`$VERSION\\`\" \\\n            --body \"Current pull request contains patched \\`CHANGELOG.md\\` file for the \\`$VERSION\\` version.\" \\\n            --base main \\\n            --head $BRANCH"
  },
  {
    "path": ".github/workflows/run-ui-tests.yml",
    "content": "# GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps:\n# - prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with UI\n# - wait for IDE to start\n# - run UI tests with separate Gradle task\n#\n# Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform\n#\n# Workflow is triggered manually.\n\nname: Run UI Tests\non:\n  workflow_dispatch\n\njobs:\n\n  testUI:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: ubuntu-latest\n            runIde: |\n              export DISPLAY=:99.0\n              Xvfb -ac :99 -screen 0 1920x1080x16 &\n              gradle runIdeForUiTests &\n          - os: windows-latest\n            runIde: start gradlew.bat runIdeForUiTests\n          - os: macos-latest\n            runIde: ./gradlew runIdeForUiTests &\n\n    steps:\n\n      # Check out current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v2.4.0\n\n      # Setup Java 11 environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v2\n        with:\n          distribution: zulu\n          java-version: 11\n          cache: gradle\n\n      # Run IDEA prepared for UI testing\n      - name: Run IDE\n        run: ${{ matrix.runIde }}\n\n      # Wait for IDEA to be started\n      - name: Health Check\n        uses: jtalk/url-health-check-action@v2\n        with:\n          url: http://127.0.0.1:8082\n          max-attempts: 15\n          retry-delay: 30s\n\n      # Run tests\n      - name: Tests\n        run: ./gradlew test"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\nbuild\n.idea\n.intellijPlatform"
  },
  {
    "path": ".run/Run IDE with Plugin.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run PhpStorm with Plugin\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"runIde\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" value=\"\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run Plugin Tests.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run Tests\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"test\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" value=\"\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run Plugin Verification.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run Verifications\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"runPluginVerifier\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" value=\"\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <method v=\"2\">\n      <option name=\"Gradle.BeforeRunTask\" enabled=\"true\" tasks=\"clean\" externalProjectPath=\"$PROJECT_DIR$\" vmOptions=\"\" scriptParameters=\"\" />\n    </method>\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run Qodana.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n    <configuration default=\"false\" name=\"Run Qodana\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n        <ExternalSystemSettings>\n            <option name=\"env\">\n                <map>\n                    <entry key=\"QODANA_SHOW_REPORT\" value=\"true\" />\n                </map>\n            </option>\n            <option name=\"executionName\" />\n            <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n            <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n            <option name=\"scriptParameters\" value=\"cleanInspections runInspections\" />\n            <option name=\"taskDescriptions\">\n                <list />\n            </option>\n            <option name=\"taskNames\">\n                <list />\n            </option>\n            <option name=\"vmOptions\" />\n        </ExternalSystemSettings>\n        <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n        <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n        <DebugAllEnabled>false</DebugAllEnabled>\n        <method v=\"2\" />\n    </configuration>\n</component>"
  },
  {
    "path": "BUILD.bazel",
    "content": "### auto-generated section `build intellij.pest` start\nload(\"@rules_jvm//:jvm.bzl\", \"jvm_library\")\n\njvm_library(\n  name = \"pest\",\n  module_name = \"intellij.pest\",\n  visibility = [\"//visibility:public\"],\n  srcs = glob([\"src/main/java/**/*.kt\", \"src/main/java/**/*.java\", \"src/main/java/**/*.form\", \"src/main/kotlin/**/*.kt\", \"src/main/kotlin/**/*.java\", \"src/main/kotlin/**/*.form\"], allow_empty = True),\n  resources = glob([\"src/main/resources/**/*\"]),\n  resource_strip_prefix = \"src/main/resources\",\n  deps = [\n    \"@community//platform/core-api:core\",\n    \"@community//platform/execution\",\n    \"@community//platform/execution-impl\",\n    \"@community//platform/ide-core-impl\",\n    \"@community//platform/platform-impl:ide-impl\",\n    \"//phpstorm/php-openapi:php\",\n    \"@community//platform/analysis-api:analysis\",\n    \"@community//platform/lang-core\",\n    \"@community//platform/projectModel-api:projectModel\",\n    \"@community//platform/remote-core\",\n    \"@community//platform/structure-view-impl:structureView-impl\",\n    \"@community//platform/util\",\n    \"@community//platform/indexing-api:indexing\",\n    \"//phpstorm/php:php-impl\",\n    \"@community//platform/analysis-impl\",\n    \"@community//platform/editor-ui-api:editor-ui\",\n    \"@community//platform/smRunner\",\n    \"@community//platform/platform-util-io:ide-util-io\",\n    \"@community//platform/ide-core\",\n    \"@community//platform/lang-api:lang\",\n    \"@community//platform/lang-impl\",\n    \"@community//xml/xml-psi-impl:psi-impl\",\n    \"@community//libraries/fastutil\",\n    \"@community//xml/dom-impl\",\n    \"@community//platform/core-ui\",\n    \"@community//platform/core-impl\",\n    \"@community//platform/util/text-matching\",\n    \"@community//platform/util:util-ui\",\n    \"@community//platform/statistics\",\n    \"@community//platform/testRunner\",\n  ]\n)\n\njvm_library(\n  name = \"pest_test_lib\",\n  testonly = True,\n  module_name = \"intellij.pest\",\n  visibility = [\"//visibility:public\"],\n  srcs = glob([], allow_empty = True),\n  runtime_deps = [\n    \":pest\",\n    \"@community//platform/core-api:core_test_lib\",\n    \"@community//platform/execution:execution_test_lib\",\n    \"@community//platform/execution-impl:execution-impl_test_lib\",\n    \"@community//platform/ide-core-impl:ide-core-impl_test_lib\",\n    \"@community//platform/platform-impl:ide-impl_test_lib\",\n    \"//phpstorm/php-openapi:php_test_lib\",\n    \"@community//platform/analysis-api:analysis_test_lib\",\n    \"@community//jps/model-api:model\",\n    \"@community//jps/model-api:model_test_lib\",\n    \"@community//platform/lang-core:lang-core_test_lib\",\n    \"@community//platform/projectModel-api:projectModel_test_lib\",\n    \"@community//platform/remote-core:remote-core_test_lib\",\n    \"@community//platform/structure-view-impl:structureView-impl_test_lib\",\n    \"@community//platform/testFramework\",\n    \"@community//platform/testFramework:testFramework_test_lib\",\n    \"@community//platform/util:util_test_lib\",\n    \"@community//platform/indexing-api:indexing_test_lib\",\n    \"//phpstorm/php:php-impl_test_lib\",\n    \"@community//platform/analysis-impl:analysis-impl_test_lib\",\n    \"@community//platform/editor-ui-api:editor-ui_test_lib\",\n    \"@community//platform/smRunner:smRunner_test_lib\",\n    \"@community//platform/platform-util-io:ide-util-io_test_lib\",\n    \"@community//platform/ide-core:ide-core_test_lib\",\n    \"@community//platform/lang-api:lang_test_lib\",\n    \"@community//platform/lang-impl:lang-impl_test_lib\",\n    \"@community//xml/xml-psi-impl:psi-impl_test_lib\",\n    \"@community//libraries/fastutil:fastutil_test_lib\",\n    \"@community//xml/dom-impl:dom-impl_test_lib\",\n    \"@community//platform/core-ui:core-ui_test_lib\",\n    \"@community//platform/core-impl:core-impl_test_lib\",\n    \"@community//platform/util/text-matching:text-matching_test_lib\",\n    \"@community//platform/util:util-ui_test_lib\",\n    \"@lib//:io-mockk\",\n    \"@lib//:io-mockk-jvm\",\n    \"@community//platform/statistics:statistics_test_lib\",\n    \"@community//platform/testRunner:testRunner_test_lib\",\n  ]\n)\n### auto-generated section `build intellij.pest` end\n\n### auto-generated section `iml intellij.pest` start\nexports_files([\n  \"intellij.pest.iml\",\n], visibility = [\"//visibility:public\"])\n### auto-generated section `iml intellij.pest` end\n\n### auto-generated section `build intellij.pest.tests` start\njvm_library(\n  name = \"pest-tests\",\n  module_name = \"intellij.pest.tests\",\n  visibility = [\"//visibility:public\"],\n  srcs = glob([], allow_empty = True)\n)\n\njvm_library(\n  name = \"pest-tests_test_lib\",\n  testonly = True,\n  visibility = [\"//visibility:public\"],\n  srcs = glob([\"src/test/kotlin/**/*.kt\", \"src/test/kotlin/**/*.java\", \"src/test/kotlin/**/*.form\"], allow_empty = True),\n  resources = glob([\"src/test/resources/**/*\"]),\n  resource_strip_prefix = \"src/test/resources\",\n  associates = [\n    \"//phpstorm/pest\",\n    \"//phpstorm/pest:pest_test_lib\",\n  ],\n  deps = [\n    \"@community//platform/core-api:core\",\n    \"@community//platform/core-api:core_test_lib\",\n    \"@community//platform/execution\",\n    \"@community//platform/execution:execution_test_lib\",\n    \"@community//platform/execution-impl\",\n    \"@community//platform/execution-impl:execution-impl_test_lib\",\n    \"@community//platform/ide-core-impl\",\n    \"@community//platform/platform-impl:ide-impl\",\n    \"//phpstorm/php-openapi:php\",\n    \"//phpstorm/php-openapi:php_test_lib\",\n    \"@community//platform/analysis-api:analysis\",\n    \"@community//platform/analysis-api:analysis_test_lib\",\n    \"@community//jps/model-api:model\",\n    \"@community//jps/model-api:model_test_lib\",\n    \"@community//platform/lang-core\",\n    \"@community//platform/lang-core:lang-core_test_lib\",\n    \"@community//platform/projectModel-api:projectModel\",\n    \"@community//platform/projectModel-api:projectModel_test_lib\",\n    \"@community//platform/remote-core\",\n    \"@community//platform/remote-core:remote-core_test_lib\",\n    \"@community//platform/structure-view-impl:structureView-impl\",\n    \"@community//platform/structure-view-impl:structureView-impl_test_lib\",\n    \"@community//platform/testFramework\",\n    \"@community//platform/testFramework:testFramework_test_lib\",\n    \"@community//platform/util\",\n    \"@community//platform/util:util_test_lib\",\n    \"@community//platform/indexing-api:indexing\",\n    \"@community//platform/indexing-api:indexing_test_lib\",\n    \"//phpstorm/php:php-impl\",\n    \"//phpstorm/php:php-impl_test_lib\",\n    \"@community//platform/analysis-impl\",\n    \"@community//platform/analysis-impl:analysis-impl_test_lib\",\n    \"@community//platform/editor-ui-api:editor-ui\",\n    \"@community//platform/editor-ui-api:editor-ui_test_lib\",\n    \"@community//platform/smRunner\",\n    \"@community//platform/smRunner:smRunner_test_lib\",\n    \"@community//platform/platform-util-io:ide-util-io\",\n    \"@community//platform/ide-core\",\n    \"@community//platform/lang-api:lang\",\n    \"@community//platform/lang-api:lang_test_lib\",\n    \"@community//platform/lang-impl\",\n    \"@community//platform/lang-impl:lang-impl_test_lib\",\n    \"@community//xml/xml-psi-impl:psi-impl\",\n    \"@community//xml/xml-psi-impl:psi-impl_test_lib\",\n    \"@community//libraries/fastutil\",\n    \"@community//libraries/fastutil:fastutil_test_lib\",\n    \"@community//plugins/coverage-common:coverage\",\n    \"@community//plugins/coverage-common:coverage_test_lib\",\n    \"@community//xml/dom-impl\",\n    \"@community//xml/dom-impl:dom-impl_test_lib\",\n    \"@community//platform/core-ui\",\n    \"@community//platform/core-ui:core-ui_test_lib\",\n    \"@community//platform/core-impl\",\n    \"@community//platform/core-impl:core-impl_test_lib\",\n    \"@community//platform/util/text-matching\",\n    \"@community//platform/util/text-matching:text-matching_test_lib\",\n    \"@community//platform/util:util-ui\",\n    \"@community//platform/util:util-ui_test_lib\",\n    \"@lib//:io-mockk\",\n    \"@lib//:io-mockk-jvm\",\n    \"@community//platform/statistics\",\n    \"@community//platform/statistics:statistics_test_lib\",\n    \"@community//platform/testRunner\",\n    \"@community//platform/testRunner:testRunner_test_lib\",\n  ],\n  runtime_deps = [\n    \":pest-tests\",\n    \"@community//platform/ide-core-impl:ide-core-impl_test_lib\",\n    \"@community//platform/platform-impl:ide-impl_test_lib\",\n    \"@community//platform/platform-util-io:ide-util-io_test_lib\",\n    \"@community//platform/ide-core:ide-core_test_lib\",\n  ]\n)\n### auto-generated section `build intellij.pest.tests` end\n\n### auto-generated section `iml intellij.pest.tests` start\nexports_files([\n  \"intellij.pest.tests.iml\",\n], visibility = [\"//visibility:public\"])\n### auto-generated section `iml intellij.pest.tests` end\n\n### auto-generated section `test intellij.pest.tests` start\nload(\"@community//build:tests-options.bzl\", \"jps_test\")\n\njps_test(\n  name = \"pest-tests_test\",\n  runtime_deps = [\":pest-tests_test_lib\"]\n)\n### auto-generated section `test intellij.pest.tests` end"
  },
  {
    "path": "CHANGELOG.md",
    "content": "<!-- Keep a Changelog guide -> https://keepachangelog.com -->\n\n# PEST IntelliJ Changelog\n\n## Unreleased\n\n### Added\n\n- Added proper resolve for custom expectations\n- Added proper rename for custom expectations\n- Added migration startup activity to delete redundant generated `Expectation.php` file\n\n### Fixed\n\n- Fixed infinite \"Closing project...\" dialog issue on project close\n\n### Changed\n\n- Reworked custom expectations engine using Symbol API \n- Removed `Expectation.php` generation\n\n## 1.11.0 - 2023-09-12\n\n### Added\n\n- Added support for running tests in describe block ([#498](https://github.com/pestphp/pest-intellij/pull/498))\n\n### Fixed\n\n- Fixed property declared dynamically showing warning in pest test cases\n- Fixed goto and rerun tests not working on new pest versions\n\n## 1.10.1 - 2023-05-31\n\n### Changed\n\n- Changed pest file creation to two actions (tests and dataset)\n\n### Added\n\n- Save test flavour preferences when creating a new test\n\n## 1.10.0 - 2023-05-31\n\n### Added\n\n- Added pest file creation support\n\n### Fixed\n\n- Remove test sources filter lookup, as it breaks others plugins\n\n## 1.9.3 - 2023-05-31\n\n### Fixed\n\n- Fixed file icon missing if all tests has property calls\n- Fixed gutter icon not updating state correctly\n- Fixed test names with `[` and `]` not being matched correctly\n- Fixed test name casing inspection not working correctly with `it` tests\n\n## 1.9.2 - 2023-03-01\n\n### Fixed\n\n- Fixed \"Preferred Coverage Engine\" not being saved\n\n## 1.9.1 - 2023-02-28\n\n### Fixed\n\n- Fixed ComposerLibraryManager being nullable now.\n- Fixed running tests with filenames containing `_`.\n\n### Changed\n\n- Changed logic for base path to be from composer.json file.\n\n## 1.9.0 - 2023-01-15\n\n### Added\n\n- Added support for running specific tests on Pest 2.0\n- Added support for running todo's as tests\n\n### Fixed\n\n- Fixed running tests with `?` in the name\n\n## 1.8.3\n\n### Added\n\n- Added support for test names with string concat statements\n- Added stacktrace folding for Pest 2.0 output\n\n### Changed\n\n- Removed the \"test started at\" text on the test console output\n\n### Fixed\n\n- Fixed regex to match tests that have both named and unnamed datasets\n\n## 1.8.1\n\n### Fixed\n\n- Fixed originalFile in iconProvider sometimes being null\n- Fixed DuplicateCustomExpectation testing crashing on unfinished inspections\n\n## 1.8.0\n\n### Added\n\n- Added support for using goto location when using remote interpreters\n\n### Fixed\n\n- Fixed nested `readAction` calls in Icon Provider\n\n### Changed\n\n- Changed Icon Provider to use indexes for better performance\n\n## 1.7.0\n\n### Added\n\n- Added `uses->in` folder reference\n- Added registry entry for disabling expectation file generation\n\n### Changed\n\n- Changed goto and completion contributor to reference provider\n- Changed icons to use build-in dark mode switching\n\n### Fixed\n\n- Fixed dataset reference error when no dataset provided yet.\n\n## 1.6.2\n\n### Fixed\n\n- Fixed duplicate type provider key with nette plugin\n\n## 1.6.1\n\n### Added\n\n- Added inspection for checking if dataset exists\n\n### Fixed\n\n- Fixed dataset autocompletion triggering on all strings\n- Fixed dataset goto triggering on all strings\n\n## 1.6.0\n\n### Added\n\n- Added converting multiple `expect` to `and` calls instead\n- Added dataset completion\n- Added dataset goto\n\n### Fixed\n\n- Fixed automatic case changing on multicased string\n\n## 1.5.0\n\n### Added\n\n- Added automatic case changing to pest test names\n\n## 1.4.2\n\n### Fixed\n\n- Changed runReadAction to runReadActionInSmartMode in startup activity\n\n## 1.4.1\n\n### Changed\n\n- Reduced custom expectation index size by over 95%\n\n### Fixed\n\n- Check if file exist in index (can happen if file is deleted outside IDE)\n- Handle path separators per OS\n\n## 1.4.0\n\n### Added\n\n- Added support for dynamic paths in `uses->in` statements\n- Added inspection for duplicate custom expectation name\n- Add surrounder for `expect`\n\n### Changed\n\n- Define root path from phpunit.xml instead of composer path\n\n### Fixed\n\n- Remove `-` from the pest generated regex\n- Escape `/` in regex method name\n\n## 1.3.0\n\n### Fixed\n\n- Changed services to light services for auto disposable\n- Fixed null pointer error when no virtual file\n\n### Changed\n\n- Change reporting on GitHub to contain full stacktracepa\n\n### Added\n\n- Added higher order expectation type provider\n- Added support for xdebug3 and xdebug2 coverage option\n\n## 1.2.2\n\n### Fixed\n\n- Hide snapshot icon for import statements\n- Fix ArrayIndex error from ExpectationFileService\n- Fixed wrong file expectation matching in ExpectationFileService\n\n### Added\n\n- Add support for  in  calls\n- Added support for running key value datasets\n\n### Changed\n\n- Changed root path for regex to be based of vendor dir location instead of working directory\n\n### Removed\n\n- Remove service message newline requirement as method is deprecated\n\n## 1.2.1\n\n### Fixed\n\n- Moved file generation into smart invocation\n\n## 1.2.0\n\n### Added\n\n- Added gutter icon for snapshots\n- Added goto snapshot file\n\n### Fixed\n\n- Rewrote the custom expectation system to use a more robust system\n- Updated custom expectation indexer to v2\n\n### Changed\n\n- Removed decorator in favor of implementing interface\n\n## 1.1.0\n\n### Fixed\n\n- Invoke the FileListener PSI part later (should fix indexing issues)\n- Fixed stub issues on PestIconProvider by wrapping `runReadAction`\n- Fixed `$this->field` not working when namespace exist\n- Fixed Concurrent modification errors on expectation file service\n- Fixed file generation triggering on projects without pest\n\n### Added\n\n- Added new context type for the root of a pest file\n- Added post fix template for `it` tests\n- Added live template for `it` test\n- Added live template for `test` test\n- Added light icon for `pest.php` file\n\n## 1.0.5\n\n### Changed\n\n- Bumped min IntelliJ version to 2021.1\n\n## 1.0.4\n\n### Added\n\n- Added Suppress inspection for `$this->field`\n\n## 1.0.3\n\n### Fixed\n\n- Fixed php type resolving during event dispatching on file listener\n- Fixed PSI and index mismatch on file listener\n\n## 1.0.2\n\n### Fixed\n\n- Fixed indexes being out of date in file listener\n\n## 1.0.1\n\n### Fixed\n\n- Removed usage of globalType (needed for 2020.3 support)\n\n## 1.0.0\n\n### Added\n\n- Added structure support for tests\n- Added autocompletion for custom expectations\n- Added pest icon for the Pest.php config file\n- Added symbol contributor for pest tests\n\n### Fixed\n\n- Fixed a read only permission bug when used with Code with me\n- Fixed wrong namespace in custom expectations file generation\n\n## 0.4.3\n\n### Added\n\n- Added IntelliJ version to bug report\n- Added new Dataset icons (Thanks @caneco!)\n- Added test state icons\n- Added run all test in file icon\n\n### Fixed\n\n- Fix support for 2021.1\n- Fix running tests with circumflex (^)\n\n### Changed\n\n- Bumped min IntelliJ version to 2020.3\n\n## 0.4.2\n\n### Added\n\n- Added path mapping support ([#77](https://github.com/pestphp/pest-intellij/pull/77))\n\n### Changed\n\n- Bumped min plugin version to 2020.2\n- Bumped Java version to 11\n\n### Removed\n\n- Disabled version checking (did not work with path mapping) ([#77](https://github.com/pestphp/pest-intellij/pull/77))\n\n### Fixed\n\n- Escape parenthesis in regex for single test ([#80](https://github.com/pestphp/pest-intellij/pull/80))\n- Suppressed expression result unused inspection when on Pest test function element. ([#84](https://github.com/pestphp/pest-intellij/pull/84))\n\n## 0.4.1\n\n### Added\n\n- Added support for auto-generated `it` test names. ([#72](https://github.com/pestphp/pest-intellij/pull/72))\n\n### Changed\n\n- Made the regex tightly bound and reused the same regex in rerun action. ([#72](https://github.com/pestphp/pest-intellij/pull/72))\n\n## 0.4.0\n\n### Added\n\n- Added support for showing pest version ([#52](https://github.com/pestphp/pest-intellij/pull/52))\n- Type provider for Pest test functions ([#48](https://github.com/pestphp/pest-intellij/pull/48))\n- Added support for navigation between tests and test subject ([#53](https://github.com/pestphp/pest-intellij/pull/53))\n- Added error reporting to GitHub issues ([#55](https://github.com/pestphp/pest-intellij/pull/55))\n- Added inspection for duplicate test names in same file. ([#56](https://github.com/pestphp/pest-intellij/pull/56))\n- Added completions for static and protected $this methods. ([#66](https://github.com/pestphp/pest-intellij/pull/66))\n- Added completions $this fields declared in beforeEach functions. ([#66](https://github.com/pestphp/pest-intellij/pull/66))\n- Added pcov coverage engine support ([#64](https://github.com/pestphp/pest-intellij/pull/64))\n\n### Fixed\n\n- Fixed duplicate test name error when no test name is given yet. ([#61](https://github.com/pestphp/pest-intellij/pull/61))\n- Fixed finding tests with namespace at the top. ([#65](https://github.com/pestphp/pest-intellij/pull/65))\n- Fixed allow location to be null in location provider. ([#68](https://github.com/pestphp/pest-intellij/pull/68))\n- Fixed rerunning tests with namespaces ([#69](https://github.com/pestphp/pest-intellij/pull/69))\n\n## 0.3.3\n\n### Fixed\n\n- Fixed running with dataset ([#47](https://github.com/pestphp/pest-intellij/pull/47))\n\n## 0.3.2\n\n### Added\n\n- Added dark/light mode icons ([#45](https://github.com/pestphp/pest-intellij/pull/45))\n\n## 0.3.1\n\n### Changed\n\n- Change the name of the plugin\n\n## 0.3.0\n\n### Added\n\n- Basic autocompletion for `$this` for PhpUnit TestCase base class ([#11](https://github.com/pestphp/pest-intellij/pull/11))\n- Line markers now works, for the whole file and the single test. ([#17](https://github.com/pestphp/pest-intellij/pull/17), [#24](https://github.com/pestphp/pest-intellij/pull/24))\n- Add running support with debugger ([#19](https://github.com/pestphp/pest-intellij/pull/19))\n- `$this->field` type support for fields declared in `beforeEach` and `beforeAll` functions ([#22](https://github.com/pestphp/pest-intellij/pull/22))\n- Run tests scoped based on the cursor ([#24](https://github.com/pestphp/pest-intellij/pull/24), [#26](https://github.com/pestphp/pest-intellij/pull/26))\n- Added support for rerun tests when using new pest version ([#39](https://github.com/pestphp/pest-intellij/pull/39))\n- Added coverage support with clover output ([#39](https://github.com/pestphp/pest-intellij/pull/39))\n\n### Changed\n\n- Migrated all Java classes to Kotlin\n\n### Fixed\n\n- Plugin require restart as PhpTestFrameworkType does not support dynamic plugins\n- Line markers reported false positives with method calls([#31](https://github.com/pestphp/pest-intellij/pull/31))\n\n## 0.1.1\n\n### Added\n\n- Initial scaffold created from [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template)\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# Pest Plugin\n\n## Mock Usage Guidelines\n\nPrefer MockK to other mocking approaches.\n\nWhen using MockK, prefer explicit stubbing over inline lambda syntax:\n\n```kotlin\nval config = mockk<RunConfiguration>()\nevery { config.project } returns project\nevery { config.name } returns \"Test\"\n```"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Pest IntelliJ\n\nThank you for wanting to contribute!\n\n## What should I know before getting started?\nThe project is coded in Kotlin using the IntelliJ platform.  \nThe IntelliJ platform has a great wiki for documentation which is recommended to get familiar for understanding\nmany of the things happening in this project.  \n[plugins.jetbrains.com/docs/intellij](https://plugins.jetbrains.com/docs/intellij/welcome.html)\n\n## How to run project locally?\n\n\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) Oliver Nybroe olivernybroe@gmail.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <img src=\"/art/banner.png\" width=\"914\" title=\"PEST IntelliJ Banner\">\n    <p align=\"center\">\n        <a href=\"https://github.com/pestphp/pest/actions\"><img alt=\"GitHub Workflow Status (master)\" src=\"https://github.com/pestphp/pest-intellij/workflows/Build/badge.svg\"></a>\n        <a href=\"https://plugins.jetbrains.com/plugin/14636-pest\"><img alt=\"Total Downloads\" src=\"https://img.shields.io/jetbrains/plugin/d/14636\"></a>\n        <a href=\"https://plugins.jetbrains.com/plugin/14636-pest\"><img alt=\"Latest Version\" src=\"https://img.shields.io/jetbrains/plugin/v/14636\"></a>\n\t    <a href=\"https://plugins.jetbrains.com/plugin/14636-pest\"><img alt=\"Latest EAP Version\" src=\"https://img.shields.io/badge/dynamic/xml?label=EAP version&query=%2Fplugin-repository%2Fcategory%2Fidea-plugin%5B1%5D%2Fversion&url=https%3A%2F%2Fplugins.jetbrains.com%2Fplugins%2Flist%3Fchannel%3Deap%26pluginId%3D14636\"></a>\n\t    <a href=\"https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub\"><img alt=\"official JetBrains project\" src=\"https://jb.gg/badges/official.svg\"></a>\n    </p>\n</p>\n\n# Pest IntelliJ\n\n<!-- Plugin description -->\nThis plugin adds support for using Pest PHP inside PHPStorm\n\n## Installation\n\n- Using IDE built-in plugin system:\n\n  <kbd>Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for \"Pest\"</kbd> >\n  <kbd>Install Plugin</kbd>\n\n- Manually:\n\n  Download the [latest release](https://github.com/pestphp/pest-intellij/releases/latest) and install it manually using\n  <kbd>Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>\n\n- Using Early Access Program (EAP) builds:\n\n  <kbd>Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Manage plugin repositories</kbd>\n\n  Add a new entry for [`https://plugins.jetbrains.com/plugins/eap/14636`](https://plugins.jetbrains.com/plugins/eap/14636)\n\n  Then search for the plugin and install it as usual.\n\n## Configuration\n\nTo configure pest to run properly, you need to setup the the proper local test framework\n\n- Navigate to\n\n  <kbd>Preferences</kbd> > <kbd>Languages & Frameworks</kbd> > <kbd>PHP</kbd> > <kbd>Test Frameworks</kbd>\n\n  And add the following two configuration fields:\n\n  Set \"Path to Pest Executable\" to\n\t<code>/path/to/your/project/vendor/bin/pest</code>\n\n  Set the \"Test Runner\" to\n\t<code>/path/to/your/project/phpunit.xml</code>\n\n\n## Resources\nFor a great video course on how to write tests with Pest, check out [Testing Laravel](https://testing-laravel.com/) or [Pest From Scratch](https://laracasts.com/series/pest-from-scratch).\n\n## Issue?\nPlease report it to [YouTrack](https://youtrack.jetbrains.com/newIssue?project=WI)\n\n## Credits\nOriginally developed by [Oliver Nybroe](https://github.com/olivernybroe)\n\n<!-- Plugin description end -->\n\n---\nPlugin based on the [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template).\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import org.jetbrains.intellij.platform.gradle.ProductMode\nimport org.jetbrains.changelog.Changelog\nimport org.jetbrains.changelog.markdownToHTML\nimport org.jetbrains.intellij.platform.gradle.TestFrameworkType\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nfun properties(key: String) = project.findProperty(key).toString()\n\nplugins {\n    // Java support\n    id(\"java\")\n    // Kotlin support\n    id(\"org.jetbrains.kotlin.jvm\") version \"2.3.0\"\n    // Gradle IntelliJ Plugin\n    id(\"org.jetbrains.intellij.platform\") version \"2.7.0\"\n    // Gradle Changelog Plugin\n    id(\"org.jetbrains.changelog\") version \"2.2.0\"\n}\n\ngroup = properties(\"pluginGroup\")\nversion = properties(\"pluginVersion\")\n\n// Configure project's dependencies\nrepositories {\n    mavenCentral()\n    intellijPlatform {\n        defaultRepositories()\n    }\n}\n\ndependencies {\n    implementation(kotlin(\"stdlib\"))\n    testImplementation(\"io.mockk:mockk:1.14.3\") {\n        exclude(\"org.jetbrains.kotlinx\", \"kotlinx-coroutines-core-jvm\")\n    }\n    testImplementation(\"junit:junit:4.13.2\")\n    intellijPlatform {\n        val type = providers.gradleProperty(\"platformType\")\n        val version = providers.gradleProperty(\"platformVersion\")\n\n        create(type, version) {\n            useInstaller = false\n            productMode = ProductMode.MONOLITH\n        }\n        testFramework(TestFrameworkType.Platform)\n        bundledPlugins(properties(\"platformBundledPlugins\").toPlugins())\n        bundledModules(properties(\"platformBundledModules\").toPlugins())\n    }\n}\n\n// Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin\nchangelog {\n    version.set(properties(\"pluginVersion\"))\n    groups.set(emptyList())\n}\n\nkotlin {\n    jvmToolchain {\n        languageVersion.set(JavaLanguageVersion.of(providers.gradleProperty(\"javaVersion\").get()))\n    }\n    compilerOptions {\n        jvmTarget.set(JvmTarget.fromTarget(providers.gradleProperty(\"javaVersion\").get()))\n    }\n}\n\ntasks {\n    withType<JavaCompile>().configureEach {\n        val javaVersion = properties(\"javaVersion\")\n        sourceCompatibility = javaVersion\n        targetCompatibility = javaVersion\n    }\n\n    wrapper {\n        gradleVersion = properties(\"gradleVersion\")\n    }\n\n    patchPluginXml {\n        version = properties(\"pluginVersion\")\n        sinceBuild.set(properties(\"pluginSinceBuild\"))\n        untilBuild.set(properties(\"pluginUntilBuild\"))\n\n        // Extract the <!-- Plugin description --> section from README.md and provide for the plugin's manifest\n        pluginDescription.set(\n            projectDir.resolve(\"README.md\").readText().lines().run {\n                val start = \"<!-- Plugin description -->\"\n                val end = \"<!-- Plugin description end -->\"\n\n                if (!containsAll(listOf(start, end))) {\n                    throw GradleException(\"Plugin description section not found in README.md:\\n$start ... $end\")\n                }\n                subList(indexOf(start) + 1, indexOf(end))\n            }.joinToString(\"\\n\").run { markdownToHTML(this) }\n        )\n\n        // Get the latest available change notes from the changelog file\n        changeNotes.set(provider {\n            changelog.renderItem(changelog.run {\n                getOrNull(properties(\"pluginVersion\")) ?: getLatest()\n            }, Changelog.OutputType.HTML)\n        })\n    }\n\n    signPlugin {\n        certificateChain.set(System.getenv(\"CERTIFICATE_CHAIN\"))\n        privateKey.set(System.getenv(\"PRIVATE_KEY\"))\n        password.set(System.getenv(\"PRIVATE_KEY_PASSWORD\"))\n    }\n\n    publishPlugin {\n        dependsOn(\"patchChangelog\")\n        token.set(System.getenv(\"PUBLISH_TOKEN\"))\n        // pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3\n        // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more:\n        // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel\n        // channels = listOf(properties(\"pluginVersion\").split('-').getOrElse(1) { \"default\" }.split('.').first())\n    }\n}\n\nprivate fun String.toPlugins(): List<String> = split(',')\n    .map(String::trim)\n    .filter(String::isNotEmpty)"
  },
  {
    "path": "coverage/BUILD.bazel",
    "content": "### auto-generated section `build intellij.pest.coverage` start\nload(\"@rules_jvm//:jvm.bzl\", \"jvm_library\")\n\njvm_library(\n  name = \"coverage\",\n  module_name = \"intellij.pest.coverage\",\n  visibility = [\"//visibility:public\"],\n  srcs = glob([\"src/**/*.kt\", \"src/**/*.java\", \"src/**/*.form\"], allow_empty = True),\n  resources = glob([\"resources/**/*\"]),\n  resource_strip_prefix = \"resources\",\n  deps = [\n    \"@lib//:kotlin-stdlib\",\n    \"//phpstorm/pest\",\n    \"//phpstorm/php:php-impl\",\n    \"@community//plugins/coverage-common:coverage\",\n    \"@community//platform/execution\",\n    \"@community//platform/smRunner\",\n    \"@community//platform/core-api:core\",\n    \"@community//platform/util\",\n    \"@community//platform/util:util-ui\",\n    \"@community//platform/ide-core\",\n    \"@community//platform/statistics\",\n    \"@community//platform/testRunner\",\n    \"//phpstorm/coverage\",\n  ]\n)\n\njvm_library(\n  name = \"coverage_test_lib\",\n  testonly = True,\n  module_name = \"intellij.pest.coverage\",\n  visibility = [\"//visibility:public\"],\n  srcs = glob([], allow_empty = True),\n  runtime_deps = [\n    \":coverage\",\n    \"//phpstorm/pest:pest_test_lib\",\n    \"//phpstorm/php:php-impl_test_lib\",\n    \"@community//plugins/coverage-common:coverage_test_lib\",\n    \"@community//platform/execution:execution_test_lib\",\n    \"@community//platform/smRunner:smRunner_test_lib\",\n    \"@community//platform/core-api:core_test_lib\",\n    \"@community//platform/util:util_test_lib\",\n    \"@community//platform/util:util-ui_test_lib\",\n    \"@community//platform/ide-core:ide-core_test_lib\",\n    \"@community//platform/statistics:statistics_test_lib\",\n    \"@community//platform/lang-api:lang\",\n    \"@community//platform/lang-api:lang_test_lib\",\n    \"@community//platform/projectModel-impl\",\n    \"@community//platform/projectModel-impl:projectModel-impl_test_lib\",\n    \"@community//platform/projectModel-api:projectModel\",\n    \"@community//platform/projectModel-api:projectModel_test_lib\",\n    \"@community//platform/platform-util-io:ide-util-io\",\n    \"@community//platform/platform-util-io:ide-util-io_test_lib\",\n    \"@community//platform/testRunner:testRunner_test_lib\",\n    \"//phpstorm/coverage:coverage_test_lib\",\n  ]\n)\n### auto-generated section `build intellij.pest.coverage` end\n\n### auto-generated section `iml intellij.pest.coverage` start\nexports_files([\n  \"intellij.pest.coverage.iml\",\n], visibility = [\"//visibility:public\"])\n### auto-generated section `iml intellij.pest.coverage` end"
  },
  {
    "path": "coverage/intellij.pest.coverage.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src\" isTestSource=\"false\" packagePrefix=\"com.intellij.pest.coverage\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" name=\"kotlin-stdlib\" level=\"project\" />\n    <orderEntry type=\"module\" module-name=\"intellij.pest\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.coverage\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.execution\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.smRunner\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util.ui\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.statistics\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.projectModel.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.projectModel\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.util.io\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.testRunner\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php.coverage\" />\n  </component>\n</module>"
  },
  {
    "path": "coverage/resources/intellij.pest.coverage.xml",
    "content": "<idea-plugin>\n    <dependencies>\n        <plugin id=\"com.jetbrains.php\"/>\n        <plugin id=\"com.pestphp.pest-intellij\"/>\n        <module name=\"intellij.php.coverage\"/>\n        <module name=\"intellij.platform.coverage\"/>\n    </dependencies>\n    \n    <extensions defaultExtensionNs=\"com.intellij\">\n        <programRunner implementation=\"com.intellij.pest.coverage.PestCoverageProgramRunner\"/>\n        <programRunner implementation=\"com.intellij.pest.coverage.features.mutate.PestMutateProgramRunner\"/>\n        <executor implementation=\"com.intellij.pest.coverage.features.mutate.PestMutateTestExecutor\" order=\"first,after debug\"/>\n        <coverageEngine implementation=\"com.intellij.pest.coverage.PestCoverageEngine\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "coverage/src/PestCoverageEnabledConfiguration.kt",
    "content": "package com.intellij.pest.coverage\n\nimport com.intellij.coverage.CoverageRunner\nimport com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration\nimport com.intellij.php.coverage.PhpUnitCoverageRunner\nimport com.pestphp.pest.configuration.PestRunConfiguration\n\nclass PestCoverageEnabledConfiguration(\n    configuration: PestRunConfiguration\n) : CoverageEnabledConfiguration(configuration, CoverageRunner.getInstance(PhpUnitCoverageRunner::class.java)) {\n    override fun coverageFileNameSeparator(): String = \"@\"\n}\n"
  },
  {
    "path": "coverage/src/PestCoverageEngine.kt",
    "content": "package com.intellij.pest.coverage\n\nimport com.intellij.coverage.CoverageFileProvider\nimport com.intellij.coverage.CoverageRunner\nimport com.intellij.coverage.CoverageSuite\nimport com.intellij.execution.configurations.RunConfigurationBase\nimport com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration\nimport com.intellij.openapi.project.Project\nimport com.intellij.php.coverage.PhpCoverageSuite\nimport com.intellij.php.coverage.PhpUnitCoverageEngine\nimport com.pestphp.pest.configuration.PestRunConfiguration\n\nclass PestCoverageEngine : PhpUnitCoverageEngine() {\n    override fun isApplicableTo(conf: RunConfigurationBase<*>): Boolean {\n        return conf is PestRunConfiguration\n    }\n\n    override fun createCoverageEnabledConfiguration(conf: RunConfigurationBase<*>): CoverageEnabledConfiguration {\n        return PestCoverageEnabledConfiguration(conf as PestRunConfiguration)\n    }\n\n    @Deprecated(\"Deprecated in Java\")\n    override fun createCoverageSuite(\n        covRunner: CoverageRunner,\n        name: String,\n        coverageDataFileProvider: CoverageFileProvider,\n        config: CoverageEnabledConfiguration\n    ): CoverageSuite? {\n        if (config is PestCoverageEnabledConfiguration) {\n            return PhpCoverageSuite(name, config.configuration.project, covRunner, coverageDataFileProvider, config.createTimestamp())\n        }\n\n        return null\n    }\n\n    override fun createCoverageSuite(\n        name: String,\n        project: Project,\n        covRunner: CoverageRunner,\n        coverageDataFileProvider: CoverageFileProvider,\n        timestamp: Long,\n        config: CoverageEnabledConfiguration\n    ): CoverageSuite? {\n        if (config is PestCoverageEnabledConfiguration) {\n            return PhpCoverageSuite(name, project, covRunner, coverageDataFileProvider, timestamp)\n        }\n\n        return null\n    }\n}\n"
  },
  {
    "path": "coverage/src/PestCoverageProgramRunner.kt",
    "content": "package com.intellij.pest.coverage\n\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.php.coverage.PhpCoverageRunner\nimport com.jetbrains.php.config.commandLine.PhpCommandSettings\nimport com.jetbrains.php.config.commandLine.PhpCommandSettingsBuilder\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.debug.xdebug.options.XdebugConfigurationOptionsManager\nimport com.jetbrains.php.phpunit.coverage.PhpUnitCoverageEngine.CoverageEngine\nimport com.jetbrains.php.run.PhpConfigurationOption\nimport com.jetbrains.php.run.PhpRunConfigurationHolder\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.features.parallel.addParallelArguments\n\nopen class PestCoverageProgramRunner : PhpCoverageRunner() {\n    companion object {\n        const val EXECUTOR_ID: String = \"Coverage\"\n        const val RUNNER_ID: String = \"PestCoverageRunner\"\n    }\n\n    override fun canRun(executorId: String, profile: RunProfile): Boolean {\n        return executorId == EXECUTOR_ID && profile is PestRunConfiguration\n    }\n\n    override fun createCoverageArguments(targetCoverage: String?): MutableList<String> {\n        val coverageArguments: ArrayList<String> = ArrayList()\n        coverageArguments.add(\"--coverage-clover\")\n        targetCoverage?.let { coverageArguments.add(it) }\n\n        return coverageArguments\n    }\n\n    override fun getRunnerId(): String = RUNNER_ID\n\n    override fun createState(\n        env: ExecutionEnvironment,\n        interpreter: PhpInterpreter,\n        runConfigurationHolder: PhpRunConfigurationHolder<*>,\n        coverageArguments: MutableList<String>,\n        localCoverage: String,\n        targetCoverage: String\n    ): RunProfileState? {\n        val runConfiguration = runConfigurationHolder.runConfiguration as PestRunConfiguration\n\n        val command = createPestCoverageCommand(runConfiguration, interpreter, coverageArguments, localCoverage, targetCoverage)\n\n        return runConfiguration.checkAndGetState(env, command)\n    }\n\n    fun createPestCoverageCommand(\n        runConfiguration: PestRunConfiguration,\n        interpreter: PhpInterpreter,\n        coverageArguments: List<String>,\n        localCoverage: String,\n        targetCoverage: String\n    ): PhpCommandSettings {\n        val command = PhpCommandSettingsBuilder(runConfiguration.project, interpreter)\n            .loadDebugExtension().build().apply {\n                runConfiguration.applyTestArguments(this, coverageArguments)\n            }\n\n        val options = when (runConfiguration.pestSettings.pestRunnerSettings.coverageEngine) {\n            CoverageEngine.XDEBUG -> XdebugConfigurationOptionsManager\n                .getConfigurationOptionsProvider(runConfiguration.project, interpreter)\n                .enableCoverage()\n                .createXdebugConfigurations()\n            CoverageEngine.PCOV -> listOf(PhpConfigurationOption(\"pcov.enabled\", 1))\n            else -> throw IllegalArgumentException(\"Unsupported coverage engine.\")\n        }\n        command.addConfigurationOptions(options)\n        addParallelArguments(runConfiguration, command)\n        setAdditionalMapping(localCoverage, targetCoverage, command)\n\n        return command\n    }\n}\n"
  },
  {
    "path": "coverage/src/features/mutate/PestMutateProgramRunner.kt",
    "content": "package com.intellij.pest.coverage.features.mutate\n\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.pest.coverage.PestCoverageProgramRunner\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.features.parallel.postprocessExecutionResult\nimport com.pestphp.pest.statistics.PestUsagesCollector\n\nprivate val MUTATE_ARGUMENTS: MutableList<String> = mutableListOf(\"--mutate\")\n\nopen class PestMutateProgramRunner : PestCoverageProgramRunner() {\n    companion object {\n        const val RUNNER_ID: String = \"PestMutateRunner\"\n    }\n\n    override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {\n        PestUsagesCollector.logMutationTestExecution(environment.project)\n        val contentDescriptor = super.doExecute(state, environment)\n        if (contentDescriptor != null) {\n            postprocessExecutionResult(contentDescriptor, environment, PestBundle.message(\"MUTATION_TESTING_IS_AVAILABLE_FROM_VERSION_3\"))\n        }\n        return contentDescriptor\n    }\n\n    override fun canRun(executorId: String, profile: RunProfile): Boolean =\n        executorId == PestMutateTestExecutor.EXECUTOR_ID && profile is PestRunConfiguration\n\n    override fun createCoverageArguments(targetCoverage: String?): MutableList<String> = MUTATE_ARGUMENTS\n\n    override fun getRunnerId(): String = RUNNER_ID\n}"
  },
  {
    "path": "coverage/src/features/mutate/PestMutateTestExecutor.kt",
    "content": "package com.intellij.pest.coverage.features.mutate\n\nimport com.intellij.execution.Executor\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.util.IconLoader.getDisabledIcon\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.openapi.util.text.TextWithMnemonic\nimport com.intellij.openapi.wm.ToolWindowId\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestIcons\nimport org.jetbrains.annotations.Nls\nimport org.jetbrains.annotations.NonNls\nimport javax.swing.Icon\n\ninternal class PestMutateTestExecutor : Executor() {\n    companion object {\n        const val EXECUTOR_ID: @NonNls String = \"PestMutateTestExecutor\"\n        const val CONTEXT_ACTION_ID: @NonNls String = \"PestRunMutate\"\n    }\n\n    override fun getToolWindowId(): String = ToolWindowId.RUN\n\n    override fun getToolWindowIcon(): Icon = AllIcons.Toolwindows.ToolWindowRun\n\n    override fun getIcon(): Icon = PestIcons.RunWithMutate\n\n    override fun getRerunIcon(): Icon = AllIcons.Actions.Rerun\n\n    override fun getDisabledIcon(): Icon = getDisabledIcon(icon)\n\n    override fun getDescription(): String = PestBundle.message(\"ACTION_RUN_SELECTED_CONFIGURATION_WITH_MUTATE_DESCRIPTION\")\n\n    override fun getActionName(): String = PestBundle.message(\"ACTION_PEST_MUTATE_TEXT\")\n\n    override fun getId(): String = EXECUTOR_ID\n\n    override fun getStartActionText(): @Nls(capitalization = Nls.Capitalization.Title) String = PestBundle.message(\"RUN_PEST_WITH_MUTATE\")\n\n    override fun getStartActionText(configurationName: String): String {\n        val configName = if (StringUtil.isEmpty(configurationName)) \"\" else \" '${shortenNameIfNeeded(configurationName)}'\"\n        return TextWithMnemonic.parse(PestBundle.message(\"RUN_S_WITH_MUTATE\")).replaceFirst(\"%s\", configName).toString()\n    }\n\n    override fun getContextActionId(): String = CONTEXT_ACTION_ID\n\n    override fun getHelpId(): String? = null\n}"
  },
  {
    "path": "coverage/tests/BUILD.bazel",
    "content": "### auto-generated section `build intellij.pest.coverage.tests` start\nload(\"@rules_jvm//:jvm.bzl\", \"jvm_library\")\n\njvm_library(\n  name = \"tests\",\n  module_name = \"intellij.pest.coverage.tests\",\n  visibility = [\"//visibility:public\"],\n  srcs = glob([], allow_empty = True),\n  runtime_deps = [\"@community//platform/ide-core\"]\n)\n\njvm_library(\n  name = \"tests_test_lib\",\n  testonly = True,\n  visibility = [\"//visibility:public\"],\n  srcs = glob([\"src/**/*.kt\", \"src/**/*.java\", \"src/**/*.form\"], allow_empty = True),\n  resources = glob([\"testData/**/*\"]),\n  resource_strip_prefix = \"testData\",\n  associates = [\n    \"//phpstorm/pest/coverage\",\n    \"//phpstorm/pest/coverage:coverage_test_lib\",\n  ],\n  deps = [\n    \"//phpstorm/pest:pest-tests\",\n    \"//phpstorm/pest:pest-tests_test_lib\",\n    \"@community//platform/lang-api:lang\",\n    \"@community//platform/lang-api:lang_test_lib\",\n    \"//phpstorm/php:php-impl\",\n    \"//phpstorm/php:php-impl_test_lib\",\n    \"//phpstorm/pest\",\n    \"//phpstorm/pest:pest_test_lib\",\n    \"@community//platform/core-api:core\",\n    \"@community//platform/core-api:core_test_lib\",\n    \"@community//platform/execution\",\n    \"@community//platform/execution:execution_test_lib\",\n    \"@community//platform/projectModel-api:projectModel\",\n    \"@community//platform/projectModel-api:projectModel_test_lib\",\n    \"@community//platform/projectModel-impl\",\n    \"@community//platform/projectModel-impl:projectModel-impl_test_lib\",\n    \"@community//platform/platform-util-io:ide-util-io\",\n    \"@community//platform/ide-core\",\n    \"@community//platform/smRunner\",\n    \"@community//platform/smRunner:smRunner_test_lib\",\n    \"@community//plugins/coverage-common:coverage\",\n    \"@community//plugins/coverage-common:coverage_test_lib\",\n    \"@community//platform/testRunner\",\n    \"@community//platform/testRunner:testRunner_test_lib\",\n    \"//phpstorm/coverage\",\n    \"//phpstorm/coverage:coverage_test_lib\",\n  ],\n  runtime_deps = [\n    \":tests\",\n    \"@community//platform/platform-util-io:ide-util-io_test_lib\",\n    \"@community//platform/ide-core:ide-core_test_lib\",\n  ]\n)\n### auto-generated section `build intellij.pest.coverage.tests` end\n\n### auto-generated section `iml intellij.pest.coverage.tests` start\nexports_files([\n  \"intellij.pest.coverage.tests.iml\",\n], visibility = [\"//visibility:public\"])\n### auto-generated section `iml intellij.pest.coverage.tests` end\n\n### auto-generated section `test intellij.pest.coverage.tests` start\nload(\"@community//build:tests-options.bzl\", \"jps_test\")\n\njps_test(\n  name = \"tests_test\",\n  runtime_deps = [\":tests_test_lib\"]\n)\n### auto-generated section `test intellij.pest.coverage.tests` end"
  },
  {
    "path": "coverage/tests/intellij.pest.coverage.tests.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/testData\" type=\"java-test-resource\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"module\" module-name=\"intellij.pest.tests\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.pest\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.pest.coverage\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.execution\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.projectModel\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.projectModel.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.util.io\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.smRunner\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.coverage\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.testRunner\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php.coverage\" scope=\"TEST\" />\n  </component>\n  <component name=\"TestModuleProperties\" production-module=\"intellij.pest.coverage\" />\n</module>"
  },
  {
    "path": "coverage/tests/src/com/intellij/pest/coverage/PestCoverageProgramRunnerTest.kt",
    "content": "package com.intellij.pest.coverage\n\nimport com.intellij.coverage.CoverageDataManager\nimport com.intellij.coverage.CoverageHelper\nimport com.intellij.execution.PsiLocation\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.psi.PsiElement\nimport com.intellij.testFramework.TestDataPath\nimport com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.PestFrameworkType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.configuration.PestRunConfigurationProducer\nimport java.nio.file.Path\nimport kotlin.io.path.exists\nimport kotlin.io.path.pathString\n\n@TestDataPath($$\"$CONTENT_ROOT/testData\")\nclass PestCoverageProgramRunnerTest : PestLightCodeFixture() {\n    private lateinit var configurationsBackup: List<PhpTestFrameworkConfiguration>\n\n    override fun getTestDataPath(): String {\n        val intellijPath = Path.of(IdeaTestExecutionPolicy.getHomePathWithPolicy(), \"phpstorm/pest/coverage/tests/testData\")\n        return if (intellijPath.exists()) {\n            intellijPath.pathString\n        } else {\n            \"testData\"\n        }\n    }\n\n    fun testCannotRunWrongExecutorId() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        assertFalse(PestCoverageProgramRunner().canRun(PestCoverageProgramRunner.EXECUTOR_ID + \"1\", configuration))\n    }\n\n    fun testCanRunFile() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        assertTrue(PestCoverageProgramRunner().canRun(PestCoverageProgramRunner.EXECUTOR_ID, configuration))\n    }\n\n    fun testCanRunFunction() = doTest {\n        val testElement = myFixture.file?.firstChild?.lastChild?.firstChild ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        assertTrue(PestCoverageProgramRunner().canRun(PestCoverageProgramRunner.EXECUTOR_ID, configuration))\n    }\n\n    fun testCanRunDirectory() = doTest {\n        val testElement = myFixture.file?.containingDirectory ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        assertTrue(PestCoverageProgramRunner().canRun(PestCoverageProgramRunner.EXECUTOR_ID, configuration))\n    }\n\n    fun testBuildFile() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n\n        val pestCoverageCommandSettings = PestCoverageProgramRunner().createPestCoverageCommand(configuration, configuration.interpreter!!, emptyList(), \"\", \"\")\n\n        val expected = \"-dxdebug.coverage_enable=1 -dxdebug.mode=coverage randomPath --teamcity /src/ATest.php\"\n        assertEquals(expected, pestCoverageCommandSettings.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    fun testBuildFunction() = doTest {\n        val testElement = myFixture.file?.firstChild?.lastChild?.firstChild ?: return@doTest\n        val configuration = createConfiguration(testElement)\n\n        val pestCoverageCommandSettings = PestCoverageProgramRunner().createPestCoverageCommand(configuration, configuration.interpreter!!, emptyList(), \"\", \"\")\n\n        val expected = \"-dxdebug.coverage_enable=1 -dxdebug.mode=coverage randomPath --teamcity /src/ATest.php\"\n        assertEquals(\n            expected,\n            pestCoverageCommandSettings.createGeneralCommandLine().parametersList.list\n                .joinToString(\" \")\n                .substringBefore(\" --filter\")\n        )\n    }\n\n    fun testBuildDirectory() = doTest {\n        val testElement = myFixture.file?.containingDirectory ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        val pestCoverageCommandSettings = PestCoverageProgramRunner().createPestCoverageCommand(configuration, configuration.interpreter!!, emptyList(), \"\", \"\")\n\n        val expected = \"-dxdebug.coverage_enable=1 -dxdebug.mode=coverage randomPath --teamcity /src\"\n        assertEquals(expected, pestCoverageCommandSettings.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    fun testBuildFileWithEnabledParallelTesting() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        configuration.pestSettings.pestRunnerSettings.parallelTestingEnabled = true\n\n        val pestCoverageCommandSettings = PestCoverageProgramRunner().createPestCoverageCommand(configuration, configuration.interpreter!!, emptyList(), \"\", \"\")\n\n        val expected = \"-dxdebug.coverage_enable=1 -dxdebug.mode=coverage randomPath --teamcity /src/ATest.php --parallel --log-teamcity php://stdout\"\n        assertEquals(expected, pestCoverageCommandSettings.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    fun testCreateCoverageSuiteOnRunningCoverageTests() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        CoverageHelper.resetCoverageSuit(configuration)\n        assertSize(1, CoverageDataManager.getInstance(project).getSuites())\n    }\n\n    private fun createConfiguration(psiElement: PsiElement): PestRunConfiguration {\n        createPestFrameworkConfiguration()\n        val context = ConfigurationContext.createEmptyContextForLocation(PsiLocation.fromPsiElement(psiElement))\n        val runConfiguration = PestRunConfigurationProducer().createConfigurationFromContext(context)?.configuration as? PestRunConfiguration\n        runConfiguration!!.settings.commandLineSettings.interpreterSettings.interpreterName = getTestName(false)\n        return runConfiguration\n    }\n\n    private fun doTest(block: () -> Unit) {\n        myFixture.configureByFile(\"ATest.php\")\n        block()\n    }\n\n    override fun setUp() {\n        super.setUp()\n        val interpreter = PhpInterpreter().apply {\n            name = getTestName(false)\n            homePath = \"$testDataPath/php\"\n        }\n        PhpInterpretersManagerImpl.getInstance(project).addInterpreter(interpreter)\n        configurationsBackup = PhpTestFrameworkSettingsManager.getInstance(project).getConfigurations(PestFrameworkType.Companion.instance)\n    }\n\n    override fun tearDown() {\n        try {\n            PhpTestFrameworkSettingsManager.getInstance(project).setConfigurations(PestFrameworkType.Companion.instance, configurationsBackup)\n            PhpInterpretersManagerImpl.getInstance(project).interpreters = emptyList()\n        } catch (e: Throwable) {\n            addSuppressedException(e)\n        } finally {\n            super.tearDown()\n        }\n    }\n}"
  },
  {
    "path": "coverage/tests/src/com/intellij/pest/coverage/features/mutate/PestMutateProgramRunnerTest.kt",
    "content": "package com.intellij.pest.coverage.features.mutate\n\nimport com.intellij.execution.PsiLocation\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.psi.PsiElement\nimport com.intellij.testFramework.TestDataPath\nimport com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.PestFrameworkType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.configuration.PestRunConfigurationProducer\nimport java.nio.file.Path\nimport kotlin.io.path.exists\nimport kotlin.io.path.pathString\n\n@TestDataPath($$\"$CONTENT_ROOT/testData/features/mutate\")\nclass PestMutateProgramRunnerTest : PestLightCodeFixture() {\n    private lateinit var configurationsBackup: List<PhpTestFrameworkConfiguration>\n\n    override fun getTestDataPath(): String {\n        val intellijPath = Path.of(IdeaTestExecutionPolicy.getHomePathWithPolicy(), \"phpstorm/pest/coverage/tests/testData/features/mutate\")\n        return if (intellijPath.exists()) {\n            intellijPath.pathString\n        } else {\n            \"testData/features/mutate\"\n        }\n    }\n\n    fun testCannotRunWrongExecutorId() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        assertFalse(PestMutateProgramRunner().canRun(PestMutateTestExecutor.EXECUTOR_ID + \"1\", configuration))\n    }\n\n    fun testCanRunFile() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        assertTrue(PestMutateProgramRunner().canRun(PestMutateTestExecutor.EXECUTOR_ID, configuration))\n    }\n\n    fun testCanRunFunction() = doTest {\n        val testElement = myFixture.file?.firstChild?.lastChild?.firstChild ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        assertTrue(PestMutateProgramRunner().canRun(PestMutateTestExecutor.EXECUTOR_ID, configuration))\n    }\n\n    fun testCanRunDirectory() = doTest {\n        val testElement = myFixture.file?.containingDirectory ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        assertTrue(PestMutateProgramRunner().canRun(PestMutateTestExecutor.EXECUTOR_ID, configuration))\n    }\n\n    private fun createConfiguration(psiElement: PsiElement): PestRunConfiguration {\n        createPestFrameworkConfiguration()\n        val context = ConfigurationContext.createEmptyContextForLocation(PsiLocation.fromPsiElement(psiElement))\n        val runConfiguration = PestRunConfigurationProducer().createConfigurationFromContext(context)?.configuration\n            as? PestRunConfiguration\n        runConfiguration!!.settings.commandLineSettings.interpreterSettings.interpreterName = getTestName(false)\n        return runConfiguration\n    }\n\n    private fun doTest(block: () -> Unit) {\n        myFixture.configureByFile(\"ATest.php\")\n        block()\n    }\n\n    override fun setUp() {\n        super.setUp()\n        val interpreter = PhpInterpreter().apply {\n            name = getTestName(false)\n            homePath = \"$testDataPath/php\"\n        }\n        PhpInterpretersManagerImpl.getInstance(project).addInterpreter(interpreter)\n        configurationsBackup = PhpTestFrameworkSettingsManager.getInstance(project).getConfigurations(PestFrameworkType.instance)\n    }\n\n    override fun tearDown() {\n        try {\n            PhpTestFrameworkSettingsManager.getInstance(project).setConfigurations(PestFrameworkType.instance, configurationsBackup)\n            PhpInterpretersManagerImpl.getInstance(project).interpreters = emptyList()\n        } catch (e: Throwable) {\n            addSuppressedException(e)\n        } finally {\n            super.tearDown()\n        }\n    }\n}\n"
  },
  {
    "path": "coverage/tests/testData/ATest.php",
    "content": "<?php\n\ntest(\"foo\", function() {});"
  },
  {
    "path": "coverage/tests/testData/features/mutate/ATest.php",
    "content": "<?php\n\ntest(\"foo\", function() {});"
  },
  {
    "path": "coverage/tests/testData/features/mutate/php",
    "content": ""
  },
  {
    "path": "coverage/tests/testData/php",
    "content": ""
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.5-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# IntelliJ Platform Artifacts Repositories\n# -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html\n\npluginGroup = com.pestphp\npluginName = PEST PHP\npluginVersion = 1.12.0\n\n# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html\n# for insight into build numbers and IntelliJ Platform versions.\npluginSinceBuild = 232.10072.32\npluginUntilBuild =\n\nplatformType = PS\nplatformVersion = LATEST-EAP-SNAPSHOT\nplatformDownloadSources = true\n# https://plugins.jetbrains.com/plugin/6610-php/versions\nplatformBundledPlugins = com.jetbrains.php\nplatformBundledModules = intellij.platform.coverage\n\n# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3\njavaVersion = 21\n\ngradleVersion = 8.13\n# Opt-out flag for bundling Kotlin standard library.\n# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.\n# suppress inspection \"UnusedProperty\"\nkotlin.stdlib.default.dependency = false\norg.gradle.jvmargs=-Xmx2048m"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=${0##*/}\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "intellij.pest.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/kotlin\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/resources\" type=\"java-resource\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.execution\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.execution.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.core.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.analysis\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.jps.model\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.projectModel\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.remote.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.structureView.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.testFramework\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.indexing\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.analysis.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.editor.ui\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.smRunner\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.util.io\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.core\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.xml.psi.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.libraries.fastutil\" />\n    <orderEntry type=\"module\" module-name=\"intellij.xml.dom.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core.ui\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core.impl\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util.text.matching\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util.ui\" />\n    <orderEntry type=\"library\" scope=\"TEST\" name=\"io.mockk\" level=\"project\" />\n    <orderEntry type=\"library\" scope=\"TEST\" name=\"io.mockk.jvm\" level=\"project\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.statistics\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.testRunner\" />\n  </component>\n</module>"
  },
  {
    "path": "intellij.pest.tests.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$/src/test/kotlin\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/kotlin\" isTestSource=\"true\" />\n    </content>\n    <content url=\"file://$MODULE_DIR$/src/test/resources\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/resources\" type=\"java-test-resource\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.execution\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.execution.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.core.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.analysis\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.jps.model\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang.core\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.projectModel\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.remote.core\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.structureView.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.testFramework\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.indexing\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.php.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.analysis.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.editor.ui\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.smRunner\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.util.io\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.ide.core\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.lang.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.xml.psi.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.libraries.fastutil\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.coverage\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.xml.dom.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core.ui\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.core.impl\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util.text.matching\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.util.ui\" scope=\"TEST\" />\n    <orderEntry type=\"library\" scope=\"TEST\" name=\"io.mockk\" level=\"project\" />\n    <orderEntry type=\"library\" scope=\"TEST\" name=\"io.mockk.jvm\" level=\"project\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.statistics\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.platform.testRunner\" scope=\"TEST\" />\n    <orderEntry type=\"module\" module-name=\"intellij.pest\" scope=\"TEST\" />\n  </component>\n  <component name=\"TestModuleProperties\" production-module=\"intellij.pest\" />\n</module>"
  },
  {
    "path": "plugin-content.yaml",
    "content": "- name: lib/modules/intellij.pest.coverage.jar\n  contentModules:\n  - name: intellij.pest.coverage\n- name: lib/pest.jar\n  modules:\n  - name: intellij.pest"
  },
  {
    "path": "settings.gradle.kts",
    "content": "rootProject.name = \"pest-intellij\"\n\npluginManagement {\n    repositories {\n        maven {\n            url = uri(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n        }\n        gradlePluginPortal()\n    }\n}"
  },
  {
    "path": "src/main/java/com/pestphp/pest/PestIcons.java",
    "content": "package com.pestphp.pest;\n\nimport com.intellij.ui.IconManager;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\n\n/**\n * NOTE THIS FILE IS AUTO-GENERATED\n * DO NOT EDIT IT BY HAND, run \"Generate icon classes\" configuration instead\n */\npublic final class PestIcons {\n  private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) {\n    return IconManager.getInstance().loadRasterizedIcon(path, PestIcons.class.getClassLoader(), cacheKey, flags);\n  }\n  /** 16x16 */ public static final @NotNull Icon Config = load(\"config.svg\", 710701582, 2);\n  /** 16x16 */ public static final @NotNull Icon Dataset = load(\"dataset.svg\", -1986461428, 2);\n  /** 16x16 */ public static final @NotNull Icon File = load(\"file.svg\", -1158724446, 2);\n  /** 16x16 */ public static final @NotNull Icon Logo = load(\"logo.svg\", -2116012898, 2);\n\n  public static final class METAINF {\n    /** 16x16 */ public static final @NotNull Icon PluginIcon = load(\"META-INF/pluginIcon.svg\", 1914567053, 0);\n  }\n\n  /** 16x16 */ public static final @NotNull Icon Run = load(\"run.svg\", -452832596, 2);\n  /** 16x16 */ public static final @NotNull Icon RunWithMutate = load(\"runWithMutate.svg\", 226904416, 2);\n}\n"
  },
  {
    "path": "src/main/java/com/pestphp/pest/configuration/PestRunConfigurationSettings.java",
    "content": "package com.pestphp.pest.configuration;\n\nimport com.intellij.util.xmlb.annotations.Property;\nimport com.intellij.util.xmlb.annotations.Transient;\nimport com.jetbrains.php.testFramework.run.PhpTestRunConfigurationSettings;\nimport com.jetbrains.php.testFramework.run.PhpTestRunnerSettings;\nimport org.jetbrains.annotations.NotNull;\n\npublic class PestRunConfigurationSettings extends PhpTestRunConfigurationSettings {\n  @Override\n  protected @NotNull PestRunnerSettings createDefault() {\n    return new PestRunnerSettings();\n  }\n\n  @Property(surroundWithTag = false)\n  public @NotNull PestRunnerSettings getPestRunnerSettings() {\n    final PhpTestRunnerSettings settings = super.getRunnerSettings();\n    if (settings instanceof PestRunnerSettings) {\n      return (PestRunnerSettings)settings;\n    }\n\n    final PestRunnerSettings copy = PestRunnerSettings.fromPhpTestRunnerSettings(settings);\n    setPestRunnerSettings(copy);\n    return copy;\n  }\n\n  public void setPestRunnerSettings(PestRunnerSettings runnerSettings) {\n    setRunnerSettings(runnerSettings);\n  }\n\n  /**\n   * @deprecated Use {@link #getPestRunnerSettings()}\n   **/\n  @Deprecated\n  @Transient\n  @Override\n  public @NotNull PhpTestRunnerSettings getRunnerSettings() {\n    return super.getRunnerSettings();\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/pestphp/pest/configuration/PhpTestRunConfiguration.java",
    "content": "package com.pestphp.pest.configuration;\n\nimport com.intellij.execution.BeforeRunTask;\nimport com.intellij.execution.configurations.ConfigurationFactory;\nimport com.intellij.openapi.project.Project;\nimport com.jetbrains.php.PhpTestFrameworkVersionDetector;\nimport com.jetbrains.php.testFramework.PhpTestFrameworkType;\nimport com.jetbrains.php.testFramework.run.PhpTestRunConfigurationHandler;\nimport com.jetbrains.php.testFramework.run.PhpTestRunnerSettingsValidator;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.List;\n\npublic abstract class PhpTestRunConfiguration extends com.jetbrains.php.testFramework.run.PhpTestRunConfiguration {\n    protected PhpTestRunConfiguration(Project project, ConfigurationFactory factory, String name, @NotNull PhpTestFrameworkType frameworkType, @NotNull PhpTestRunnerSettingsValidator validator, @NotNull PhpTestRunConfigurationHandler handler, @Nullable PhpTestFrameworkVersionDetector versionDetector) {\n        super(project, factory, name, frameworkType, validator, handler, versionDetector);\n    }\n\n    @Override\n    public void setBeforeRunTasks(@NotNull List<BeforeRunTask<?>> value) {\n        super.setBeforeRunTasks(value);\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/FileUtil.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.psi.PsiFile\nimport com.intellij.testFramework.LightVirtualFile\n\n/**\n * Takes care of getting the path of a file even if it's a light file.\n */\nval PsiFile.realPath: String\n    get() {\n        var virtualFile = this.viewProvider.virtualFile\n        if (virtualFile is LightVirtualFile && virtualFile.originalFile != null) {\n            virtualFile = virtualFile.originalFile\n        }\n        return virtualFile.path\n    }"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestBundle.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.DynamicBundle\nimport org.jetbrains.annotations.NonNls\nimport org.jetbrains.annotations.PropertyKey\nimport java.util.function.Supplier\n\n@NonNls\nprivate const val BUNDLE = \"messages.pestBundle\"\n\nobject PestBundle : DynamicBundle(BUNDLE) {\n    @Suppress(\"SpreadOperator\")\n    @JvmStatic\n    fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) = getMessage(key, *params)\n\n    @JvmStatic\n    fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): Supplier<String> {\n        return getLazyMessage(key, *params)\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestComposerConfig.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.execution.configurations.ConfigurationType\nimport com.jetbrains.php.testFramework.PhpTestFrameworkComposerConfig\nimport com.pestphp.pest.configuration.PestRunConfigurationType.Companion.instance\n\nclass PestComposerConfig : PhpTestFrameworkComposerConfig(PestFrameworkType.instance, PACKAGE, RELATIVE_PATH) {\n    override fun getDefaultConfigName(): String {\n        return \"phpunit.xml\"\n    }\n\n    override fun getConfigurationType(): ConfigurationType {\n        return instance\n    }\n\n    companion object {\n        private const val PACKAGE = \"pestphp/pest\"\n        private const val RELATIVE_PATH = \"pestphp/pest/bin/pest\"\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestFrameworkType.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.php.testFramework.PhpTestDescriptor\nimport com.jetbrains.php.testFramework.PhpTestFrameworkFormDecorator\nimport com.jetbrains.php.testFramework.PhpTestFrameworkFormDecorator.PhpDownloadableTestFormDecorator\nimport com.jetbrains.php.testFramework.PhpTestFrameworkType\nimport com.jetbrains.php.testFramework.ui.PhpTestFrameworkBaseConfigurableForm\nimport com.jetbrains.php.testFramework.ui.PhpTestFrameworkBySdkConfigurableForm\nimport com.jetbrains.php.testFramework.ui.PhpTestFrameworkConfigurableForm\nimport com.pestphp.pest.configuration.PestVersionDetector\nimport org.jetbrains.annotations.Nls\nimport org.jetbrains.annotations.NonNls\nimport javax.swing.Icon\n\n/**\n * Registers a framework type for PHP.\n *\n * This class is used to show the menu in\n * `Preferences -> Languages & Frameworks -> PHP -> Test Frameworks`\n */\nclass PestFrameworkType : PhpTestFrameworkType() {\n    private val pestUrl = \"https://github.com/pestphp/pest/releases\"\n\n    override fun getDisplayName(): @Nls String {\n        return PestBundle.message(\"FRAMEWORK_NAME\")\n    }\n\n    override fun getID(): String {\n        return ID\n    }\n\n    override fun getIcon(): Icon {\n        return PestIcons.Logo\n    }\n\n    override fun getDecorator(): PhpTestFrameworkFormDecorator {\n        return object : PhpDownloadableTestFormDecorator(pestUrl) {\n            override fun decorate(\n                project: Project,\n                form: PhpTestFrameworkBaseConfigurableForm<*>\n            ): PhpTestFrameworkConfigurableForm<*> {\n                if (form !is PhpTestFrameworkBySdkConfigurableForm) {\n                    form.setVersionDetector(PestVersionDetector.instance)\n                }\n                return super.decorate(project, form)\n            }\n        }\n    }\n\n    override fun getDescriptor(): PhpTestDescriptor {\n        return PestTestDescriptor\n    }\n\n    companion object {\n        @NonNls\n        val ID = \"Pest\"\n        val instance: PhpTestFrameworkType\n            get() = getTestFrameworkType(ID)\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestFunctionsUtil.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.jetbrains.php.PhpIndex\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.Function\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.GroupStatement\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.PhpExpression\nimport com.jetbrains.php.lang.psi.elements.PhpNamespace\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\n\nval PEST_TEST_CALL_TYPE = PhpType.from(\n    \"\\\\Pest\\\\PendingCalls\\\\TestCall\", // for Pest versions >= 2.x\n    \"\\\\Pest\\\\PendingObjects\\\\TestCall\" // for Pest versions 1.x\n)\n\nfun PsiElement?.isPestTestReference(isSmart: Boolean = false): Boolean {\n    return when (this) {\n        null -> false\n        is MethodReference -> this.isPestTestMethodReference(isSmart)\n        is FunctionReferenceImpl -> this.isPestTestFunction(isSmart)\n        else -> false\n    }\n}\n\nprivate val testNames = setOf(\"it\", \"test\", \"todo\", \"describe\", \"arch\")\nfun FunctionReferenceImpl.isPestTestFunction(isSmart: Boolean = false): Boolean {\n    if (this.canonicalText !in testNames) return false\n    return !isSmart || (this.resolveLocal().isEmpty() && PhpIndex.getInstance(project).getFunctionsByName(this.canonicalText).any { function ->\n        PEST_TEST_CALL_TYPE.isConvertibleFromGlobal(project, function.type)\n    })\n}\n\nfun FunctionReferenceImpl.isPestBeforeFunction(): Boolean {\n    return this.canonicalText == \"beforeEach\"\n}\n\nfun FunctionReferenceImpl.isPestAfterFunction(): Boolean {\n    return this.canonicalText == \"afterEach\"\n}\n\nprivate val allPestNames = setOf(\"it\", \"test\", \"todo\", \"beforeEach\", \"afterEach\", \"dataset\", \"describe\", \"arch\")\nfun FunctionReferenceImpl.isAnyPestFunction(): Boolean {\n    return this.canonicalText in allPestNames\n}\n\nfun FunctionReferenceImpl.isDescribeFunction(): Boolean {\n    return this.canonicalText == \"describe\"\n}\n\n\nfun MethodReference.isPestTestMethodReference(isSmart: Boolean = false): Boolean {\n    return when (val reference = classReference) {\n        is FunctionReferenceImpl -> reference.isPestTestFunction(isSmart)\n        is MethodReference -> reference.isPestTestMethodReference(isSmart)\n        is FieldReference -> reference.isPestTestMethodReference(isSmart)\n        else -> false\n    }\n}\n\nfun FieldReference.isPestTestMethodReference(isSmart: Boolean = false): Boolean {\n    return when (val reference = classReference) {\n        is FunctionReferenceImpl -> reference.isPestTestFunction(isSmart)\n        is MethodReference -> reference.isPestTestMethodReference(isSmart)\n        is FieldReference -> reference.isPestTestMethodReference(isSmart)\n        else -> false\n    }\n}\n\nfun PsiFile.getRoot(): List<PsiElement> {\n    val element = this.firstChild\n\n    return element.children.filterIsInstance<PhpNamespace>()\n        .mapNotNull { it.statements }\n        .getOrElse(\n            0\n        ) { element }\n        .children\n        .filterIsInstance<Statement>()\n        .mapNotNull { it.firstChild }\n}\n\n/**\n * Traverses elements and recursively enters describe blocks, collecting items via the collector function.\n */\ninternal fun <T> collectFromDescribeBlocks(\n    elements: List<PhpPsiElement>,\n    collector: (PhpPsiElement) -> T?\n): List<T> {\n    val result = mutableListOf<T>()\n\n    for (element in elements) {\n        collector(element)?.let { result.add(it) }\n\n        val funcRef = element as? FunctionReferenceImpl\n        if (funcRef != null && funcRef.isDescribeFunction()) {\n            val closure = (funcRef.parameters.getOrNull(1) as? PhpExpression)?.firstChild as? Function\n            val body = closure?.children?.filterIsInstance<GroupStatement>()?.firstOrNull()\n            val statements = body?.statements?.mapNotNull { it.firstChild }?.filterIsInstance<PhpPsiElement>() ?: emptyList()\n            result.addAll(collectFromDescribeBlocks(statements, collector))\n        }\n    }\n\n    return result\n}\n\nfun PsiFile.getPestTests(isSmart: Boolean = false): Set<FunctionReference> {\n    return collectFromDescribeBlocks(this.getRootPhpPsiElements()) { element ->\n        if (element.isPestTestReference(isSmart)) element as? FunctionReference else null\n    }.toSet()\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestIconProvider.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.ide.FileIconProvider\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.pestphp.pest.features.datasets.isIndexedPestDatasetFile\nimport javax.swing.Icon\n\nclass PestIconProvider : FileIconProvider {\n    override fun getIcon(vFile: VirtualFile, flags: Int, project: Project?): Icon? {\n        if (project == null || DumbService.isDumb(project)) return null\n        val file = PsiManager.getInstance(project).findFile(vFile) as? PhpFile ?: return null\n        if (file.isIndexedPestTestFile()) {\n            return PestIcons.File\n        }\n        if (file.isIndexedPestDatasetFile()) {\n           return PestIcons.Dataset\n        }\n        if (file.isPestFile()) {\n            return PestIcons.Logo\n        }\n        return null\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestNamingUtil.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.findParentOfType\nimport com.intellij.psi.util.parents\nimport com.intellij.remote.RemoteSdkProperties\nimport com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl\nimport com.jetbrains.php.lang.psi.elements.ArrayCreationExpression\nimport com.jetbrains.php.lang.psi.elements.ClassConstantReference\nimport com.jetbrains.php.lang.psi.elements.ClassReference\nimport com.jetbrains.php.lang.psi.elements.ConcatenationExpression\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.ParameterListOwner\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.PhpReference\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.run.remote.PhpRemoteInterpreterManager\nimport com.jetbrains.php.util.pathmapper.PhpPathMapper\nimport com.pestphp.pest.runner.getLocationUrl\nimport java.util.Locale\n\nfun FunctionReferenceImpl.getPestTestName(): String? {\n    val testName = getParameter(0)?.stringValue ?: return tryGetArchTestName(this)\n\n    val parent = this.findParentOfType<FunctionReferenceImpl>()\n    val prepend = if (parent is FunctionReferenceImpl && parent.isDescribeFunction()) {\n        parent.getPestTestName()\n    } else {\n        \"\"\n    }\n\n    return when (this.canonicalText) {\n        \"it\" -> \"${prepend}it $testName\"\n        \"describe\" -> \"${prepend}`$testName` → \"\n        else -> \"${prepend}$testName\"\n    }\n}\n\nprivate fun tryGetArchTestName(functionReference: FunctionReference): String? =\n    if (functionReference.canonicalText == \"arch\") {\n        getArchTestName(functionReference)\n    } else {\n        null\n    }\n\nprivate fun getArchTestName(functionReference: FunctionReference): String {\n    val parents = functionReference.parents(false).takeWhile { it !is Statement }.toList()\n    return parents.joinToString(separator = \" → \") { element ->\n        val name = if (element is PhpReference) element.canonicalText else element.text\n        val parameters = if (element is ParameterListOwner) getParametersString(element) else \"\"\n        \"$name$parameters\"\n    }\n}\n\nprivate fun getParametersString(element: ParameterListOwner) =\n    \" \" + when (val elem = element.parameters.firstOrNull()) {\n        is ArrayCreationExpression -> elem.children.filterIsInstance<PhpPsiElement>().joinToString(prefix = \"[\", postfix = \"]\") { it.text }\n        is StringLiteralExpression -> elem.text\n        else -> \"\"\n    }.replace(\"\\\"\", \"'\")\n\nval PsiElement.stringValue: String?\n    get() = when (this) {\n        is StringLiteralExpression -> this.contents\n        is ConcatenationExpression -> this.contents\n        is ClassConstantReference -> {\n            val classRef = this.classReference\n            if (classRef is ClassReference && this.isStatic && this.lastChild.text == \"class\") {\n                classRef.fqn\n                    ?.removePrefix(\"\\\\\")\n                    ?.replace(\"\\\\\", \"\\\\\\\\\")\n            } else null\n        }\n        else -> null\n    }\n\nval ConcatenationExpression.contents: String?\n    get() {\n        val left = this.leftOperand?.stringValue\n        val right = this.rightOperand?.stringValue\n\n        if (left === null || right === null) {\n            return null\n        }\n\n        return left + right\n    }\n\nfun PsiElement?.getPestTestName(): String? {\n    return when (this) {\n        is MethodReference -> (this.classReference as? FunctionReference)?.getPestTestName()\n        is FunctionReferenceImpl -> this.getPestTestName()\n        else -> null\n    }\n}\n\nfun PsiElement?.getInitialFunctionReference(): FunctionReference? {\n    return when (this) {\n        is MethodReference -> (this.classReference as? FunctionReference).getInitialFunctionReference()\n        is FunctionReferenceImpl -> this\n        else -> null\n    }\n}\n\nfun PsiElement.toPestTestRegex(workingDirectory: String): String? {\n    return this.getPestTestName()?.toPestTestRegex(\n        workingDirectory,\n        this.containingFile.virtualFile.path,\n        PhpPathMapper.create(this.project)\n    )\n}\n\nfun PsiElement.toPestFqn(): List<String> {\n    val testName = this.getPestTestName() ?: return emptyList()\n\n    val file = this.containingFile.virtualFile.path\n\n    return PhpInterpretersManagerImpl.getInstance(this.project)\n        .interpreters\n        .asSequence()\n        .map { it.phpSdkAdditionalData }\n        .filter { it is RemoteSdkProperties }\n        .mapNotNull {\n            PhpRemoteInterpreterManager.getInstance()?.createPathMappings(\n                this.project,\n                it\n            )\n        }\n        .map { it.convertToRemote(file) }\n        .map { \"pest_qn://$it::$testName\" }\n        .plus(\"${getLocationUrl(this.containingFile)}::$testName\")\n        .toList()\n}\n\nfun String.toPestTestRegex(rootPath: String, file: String, pathMapper: PhpPathMapper): String {\n    val mappedWorkingDirectory = pathMapper.getRemoteFilePath(rootPath) ?: rootPath\n    val mappedFile = pathMapper.getRemoteFilePath(file) ?: file\n\n    // Follow the steps for class name generation\n    // 1. Take the path of the PEST file from the cwd.\n    val fqn = mappedFile.withoutFirstFileSeparator\n        .removePrefix(mappedWorkingDirectory.withoutFirstFileSeparator)\n        .withoutFirstFileSeparator\n        // 2. Make the first folder's first letter uppercase.\n        .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }\n        // 3. Remove file extension (.php) and compound suffixes (.test, .spec, etc.) from filename.\n        .removeSuffix(\".php\")\n        .let { path ->\n            truncateAtFirstDot(path)\n        }\n        // 4. Make directory separators to namespace separators.\n        .replace(\"\\\\\", \"\\\\\\\\\")\n        .replace(\"/\", \"\\\\\\\\\")\n        // 5. Remove unsupported characters (keep only alphanumeric and escaped backslashes).\n        .replace(Regex(\"[^A-Za-z0-9\\\\\\\\]\"), \"\")\n        // 6. Add P as a namespace before the generated namespace.\n        .let { \"(P\\\\\\\\)?$it\" }\n\n    // Allow substring matching only for \"describe\" block execution\n    val possibleEndOfLine = if (this.endsWith(\" → \")) \"\" else \"$\"\n\n    // Escape characters\n    val testName = this\n        .replace(\" \", \"\\\\s\")\n        .replace(\"(\", \"\\\\(\")\n        .replace(\")\", \"\\\\)\")\n        .replace(\"[\", \"\\\\[\")\n        .replace(\"]\", \"\\\\]\")\n        .replace(\"^\", \"\\\\^\")\n        .replace(\"/\", \"\\\\/\")\n        .replace(\"?\", \"\\\\?\")\n        .replace(\"+\", \"\\\\+\")\n\n    // Match the description of a single data set\n    val dataSet = \"\"\"(data\\sset\\s\".*\"|\\(.*\\))\"\"\"\n\n    return \"\"\"^$fqn::$testName(\\swith\\s$dataSet(\\s\\/\\s$dataSet)*(\\s#\\d+)?)?$possibleEndOfLine\"\"\"\n}\n\nprivate fun truncateAtFirstDot(path: String): String {\n    val lastSep = maxOf(path.lastIndexOf('/'), path.lastIndexOf('\\\\'))\n    return if (lastSep >= 0) {\n        val dir = path.substring(0, lastSep + 1)\n        val filename = path.substring(lastSep + 1).substringBefore('.')\n        dir + filename\n    }\n    else {\n        path.substringBefore('.')\n    }\n}\n\nval String.withoutFirstFileSeparator: String\n    get() {\n        return this.removePrefix(\"/\").removePrefix(\"\\\\\")\n    }\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestNewTestFromClassAction.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.jetbrains.php.phpunit.codeGeneration.PhpNewTestAction\nimport com.jetbrains.php.testFramework.PhpTestCreateInfo\n\nopen class PestNewTestFromClassAction: PhpNewTestAction(PestBundle.messagePointer(\"action.Pest.New.File.text\"),\n                                                   PestBundle.messagePointer(\"ACTIONS_NEW_PEST_TEST_ACTION_DESCRIPTION\"),\n                                                   PestIcons.Logo) {\n    override fun getDefaultTestCreateInfo(project: Project, locationContext: VirtualFile?): PhpTestCreateInfo {\n        return PestTestCreateInfo\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestPluginDisposable.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\n\n/**\n * The service is intended to be used instead of a project/application as a parent disposable.\n */\n@Service(Service.Level.APP, Service.Level.PROJECT)\nclass PestPluginDisposable : Disposable {\n    override fun dispose() {}\n\n    companion object {\n        @JvmStatic\n        fun getInstance(): Disposable = ApplicationManager.getApplication().getService(PestPluginDisposable::class.java)\n\n        @JvmStatic\n        fun getInstance(project: Project): Disposable = project.getService(PestPluginDisposable::class.java)\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestSettings.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.components.PersistentStateComponent\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.components.State\nimport com.intellij.openapi.components.Storage\nimport com.intellij.openapi.components.service\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.xmlb.XmlSerializerUtil\nimport com.pestphp.pest.parser.PestConfigurationFile\nimport com.pestphp.pest.parser.PestConfigurationFileParser\n\n@Service(Service.Level.PROJECT)\n@State(name = \"PestSettings\", storages = [Storage(\"pest.xml\")])\nclass PestSettings : PersistentStateComponent<PestSettings> {\n    var pestFilePath = \"tests/Pest.php\"\n    var preferredTestFlavor =  TestFlavor.IT\n\n    enum class TestFlavor {\n        IT,\n        TEST\n    }\n\n    override fun getState(): PestSettings {\n        return this\n    }\n\n    override fun loadState(state: PestSettings) {\n        XmlSerializerUtil.copyBean(state, this)\n    }\n\n    private val parser = PestConfigurationFileParser(this)\n\n    fun getPestConfiguration(project: Project, virtualFile: VirtualFile? = null): PestConfigurationFile {\n        return parser.parse(project, virtualFile)\n    }\n\n    companion object {\n        fun getInstance(project: Project): PestSettings {\n            return project.service()\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestTestCreateInfo.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.testFramework.PhpUnitAbstractTestCreateInfo\nimport com.pestphp.pest.inspections.convertTestNameToSentenceCase\nimport javax.swing.Icon\n\nconst val INTERNAL_PEST_FILE_TEMPLATE_NAME = \"Pest file from class\"\n\nobject PestTestCreateInfo : PhpUnitAbstractTestCreateInfo() {\n    override fun getName(): String {\n        return \"Pest\"\n    }\n\n    override fun getTemplateName(): String {\n        return INTERNAL_PEST_FILE_TEMPLATE_NAME\n    }\n\n    override fun getIcon(): Icon {\n        return PestIcons.Logo\n    }\n\n    override fun getTestMethodText(project: Project, classFqn: String, methodName: String): String {\n        return \"test('${convertTestNameToSentenceCase(methodName)}', function(){})\"\n    }\n\n    override fun shouldPostprocessTemplateFile(): Boolean {\n        return true\n    }\n\n    override fun postprocessTemplateFile(file: PsiFile) {\n        val test = PsiTreeUtil.findChildOfType(file, FunctionReference::class.java)\n        test?.parent?.delete()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestTestDescriptor.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.util.SmartList\nimport com.jetbrains.php.lang.psi.elements.Method\nimport com.jetbrains.php.lang.psi.elements.PhpClass\nimport com.jetbrains.php.phpunit.PhpUnitTestDescriptor\nimport com.jetbrains.php.testFramework.PhpTestCreateInfo\nimport java.util.Collections\n\n/**\n * findTests, findClasses, and findMethods return empty collections,\n * since Pest tests are function calls, not methods, and therefore are not located in PHP classes\n */\nobject PestTestDescriptor : PhpUnitTestDescriptor() {\n    override fun findTests(clazz: PhpClass): MutableCollection<PhpClass> {\n        return Collections.emptySet()\n    }\n\n    override fun findTests(method: Method): MutableCollection<Method> {\n        return Collections.emptySet()\n    }\n\n    override fun findClasses(test: PhpClass, testName: String): MutableCollection<PhpClass> {\n        return Collections.emptySet()\n    }\n\n    override fun findMethods(testMethod: Method): MutableCollection<Method> {\n        return Collections.emptySet()\n    }\n\n    override fun getTestCreateInfos(): MutableCollection<PhpTestCreateInfo> {\n        return SmartList(PestTestCreateInfo)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestTestFileUtil.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.util.Key\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.CachedValue\nimport com.intellij.psi.util.CachedValueProvider\nimport com.intellij.psi.util.CachedValuesManager\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.jetbrains.php.lang.psi.elements.AssignmentExpression\nimport com.jetbrains.php.lang.psi.elements.ClassConstantReference\nimport com.jetbrains.php.lang.psi.elements.ClassReference\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.Function\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.ParameterList\nimport com.jetbrains.php.lang.psi.elements.Variable\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\n\nval CONFIGURATION_FUNCTIONS = listOf(\"pest\", \"uses\")\nval CONFIGURATION_METHODS = listOf(\"use\", \"uses\", \"extend\", \"extends\")\n\nfun PsiElement?.isThisVariableInPest(condition: (FunctionReferenceImpl) -> Boolean): Boolean {\n    if ((this as? Variable)?.name != \"this\") return false\n\n    var psiElement = this\n    while (true) {\n        val functionReference = getOuterFunctionReference(psiElement) ?: return false\n        if (condition(functionReference)) {\n            return true\n        }\n        psiElement = functionReference\n    }\n}\n\nfun PsiElement?.isTestAsThisVariableInPest(condition: (FunctionReferenceImpl) -> Boolean): Boolean {\n    val functionReference = this as? FunctionReference ?: return false\n\n    if (functionReference.name != \"test\" || !functionReference.parameters.isEmpty()) return false\n\n    return getOuterFunctionReference(this)?.let { functionReferenceImpl -> condition(functionReferenceImpl) } ?: false\n}\n\nprivate fun getOuterFunctionReference(element: PsiElement?): FunctionReferenceImpl? {\n    val closure = PsiTreeUtil.getParentOfType(element, Function::class.java)\n\n    if (closure == null || !closure.isClosure) return null\n\n    val parameterList = closure.parent?.parent as? ParameterList ?: return null\n\n    if (parameterList.parent !is FunctionReferenceImpl) return null\n\n    return parameterList.parent as FunctionReferenceImpl\n}\n\nfun PsiFile.getAllBeforeThisAssignments(): List<AssignmentExpression> {\n    return this.getRoot()\n        .filterIsInstance<FunctionReferenceImpl>()\n        .filter { it.isPestBeforeFunction() }\n        .flatMap { it.getThisStatements() }\n}\n\nprivate val cacheKey = Key<CachedValue<List<AssignmentExpression>>>(\"com.pestphp.pest_assignments\")\nprivate fun FunctionReferenceImpl.getThisStatements(): List<AssignmentExpression> {\n    return CachedValuesManager.getCachedValue(this, cacheKey) {\n        val result = PsiTreeUtil.findChildrenOfType(\n            this.parameterList?.getParameter(0),\n            AssignmentExpression::class.java\n        )\n            .filter { ((it.variable as? FieldReference)?.classReference as? Variable)?.name == \"this\" }\n\n        CachedValueProvider.Result.create(result, this)\n    }\n}\n\nfun FunctionReference.getConfigurationPhpType(): PhpType? {\n    if (this.name !in CONFIGURATION_METHODS) return PhpType()\n    parameters.mapNotNull {\n        val classRef = it as? ClassConstantReference ?: return@mapNotNull null\n\n        if (classRef.name != \"class\") return@mapNotNull null\n\n        (classRef.classReference as? ClassReference)?.fqn\n    }.apply {\n        if (this.isEmpty()) return null\n\n        val res = PhpType()\n\n        this.forEach {\n            res.add(it)\n        }\n\n        return res\n    }\n}\n\nfun FunctionReference.getPestConfigurationPhpType(): PhpType? {\n    if (this is FunctionReferenceImpl && this.name in CONFIGURATION_FUNCTIONS) {\n        return this.getConfigurationPhpType()\n    }\n    val classReference = (this as? MethodReference)?.classReference ?: return null\n    if (classReference is FunctionReference) {\n        val typeFromClassRef = classReference.getPestConfigurationPhpType()\n        val typeFromParameters = this.getConfigurationPhpType()\n        return PhpType().add(typeFromParameters).add(typeFromClassRef)\n    }\n    return null\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestTestRunLineMarkerProvider.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.execution.lineMarker.RunLineMarkerContributor\nimport com.intellij.icons.AllIcons.RunConfigurations.TestState.Run\nimport com.intellij.icons.AllIcons.RunConfigurations.TestState.Run_run\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.lexer.PhpTokenTypes\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\n\n/**\n * Adds markers on the line on the left side for running a pest specific pest test.\n */\nclass PestTestRunLineMarkerProvider : RunLineMarkerContributor() {\n    override fun getInfo(leaf: PsiElement): Info? {\n        if (!leaf.containingFile.isPestTestFile(isSmart = true)) {\n            return null\n        }\n\n        // Handle icons if the reference is a pest test.\n        if (isPestTestReference(leaf)) {\n            return getPestTest(\n                leaf.parent as FunctionReferenceImpl,\n                leaf.project,\n            )\n        }\n\n        // Handle icon for running all tests in the file.\n        if (PhpPsiUtil.isOfType(leaf, PhpTokenTypes.PHP_OPENING_TAG)) {\n            return withExecutorActions(Run_run)\n        }\n\n        return null\n    }\n\n    private fun isPestTestReference(leaf: PsiElement): Boolean {\n        if (PhpPsiUtil.isOfType(leaf, PhpTokenTypes.IDENTIFIER)) {\n            (leaf.parent as? FunctionReferenceImpl)?.let { functionReference ->\n                if (!functionReference.isAnyPestFunction()) {\n                    return false\n                }\n                val statementChild = PhpPsiUtil.getParentOfClass(functionReference, true, Statement::class.java)?.firstChild\n                val outerFunctionReference = PhpPsiUtil.getParentByCondition<FunctionReferenceImpl>(\n                    statementChild,\n                    { it is FunctionReferenceImpl },\n                    PhpFile.INSTANCEOF\n                )\n                if (outerFunctionReference == null || outerFunctionReference.isDescribeFunction()) {\n                    return statementChild is FunctionReference && statementChild.isPestTestReference(isSmart = true)\n                }\n            }\n        }\n        return false\n    }\n\n    private fun getPestTest(reference: FunctionReferenceImpl, project: Project): Info {\n        val fqn = reference.toPestFqn()\n\n        val icon = fqn.firstOrNull { getTestStateIcon(it, project, false) !== Run }\n            ?.let { getTestStateIcon(it, project, false) } ?: Run\n\n        return withExecutorActions(icon)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/PestUtil.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.Key\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.psi.util.CachedValue\nimport com.intellij.psi.util.CachedValueProvider\nimport com.intellij.psi.util.CachedValuesManager\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.composer.configData.ComposerConfigManager\nimport com.jetbrains.php.lang.psi.PhpExpressionCodeFragment\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.PhpNamespace\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.phpunit.PhpUnitUtil\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.indexers.key\n\nval PEST_TEST_FILE_KEY = Key<CachedValue<Boolean>>(\"isPestTestFile\")\nval PEST_TEST_FILE_SMART_KEY = Key<CachedValue<Boolean>>(\"smart isPestTestFile\")\n\nfun PsiFile.isPestTestFile(isSmart: Boolean = false): Boolean {\n    if (this !is PhpFile || this is PhpExpressionCodeFragment) return false\n    return CachedValuesManager.getCachedValue(this, if (isSmart) PEST_TEST_FILE_SMART_KEY else PEST_TEST_FILE_KEY) {\n        val isPestTestFile = this.getRootPhpPsiElements().any { psiElement -> psiElement.isPestTestReference(isSmart) }\n        CachedValueProvider.Result.create(isPestTestFile, this)\n    }\n}\n\nfun PsiFile.isIndexedPestTestFile(): Boolean {\n    return FileBasedIndex.getInstance().getValues(\n        key,\n        this.realPath,\n        ProjectScope.getProjectScope(this.project)\n    ).isNotEmpty() && this.isPestTestFile(isSmart = true)\n}\n\nfun PsiFile.isPestConfigurationFile(): Boolean {\n    return PhpUnitUtil.isPhpUnitConfigurationFile(this)\n}\n\nfun Project.isPestEnabled(): Boolean {\n    return PhpTestFrameworkSettingsManager\n        .getInstance(this)\n        .getConfigurations(PestFrameworkType.instance)\n        .any { StringUtil.isNotEmpty(it.executablePath) }\n}\n\nfun PsiFile.getRootPhpPsiElements(): List<PhpPsiElement> {\n    if (this !is PhpFile) return listOf()\n\n    val element = this.firstChild\n\n    return element.children.filterIsInstance<PhpNamespace>()\n        .mapNotNull { it.statements }\n        .getOrElse(\n            0\n        ) { element }\n        .children\n        .filterIsInstance<Statement>()\n        .mapNotNull { it.firstChild }\n        .filterIsInstance<PhpPsiElement>()\n}\n\n/**\n * Checks if the file is the `tests/Pest.php` file.\n */\nfun PsiFile.isPestFile(): Boolean {\n    val baseDir = getBaseDir(this.project, this.virtualFile) ?: return false\n\n    val pestFilePath = PestSettings.getInstance(this.project).pestFilePath\n\n    return this.virtualFile?.path == baseDir.path + \"/\" + pestFilePath\n}\n\nfun getBaseDir(project: Project, virtualFile: VirtualFile? = null): VirtualFile? {\n    return ComposerConfigManager.getInstance(project).getConfig(virtualFile)?.parent\n        ?: project.guessProjectDir()\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/annotator/PestAnnotator.kt",
    "content": "package com.pestphp.pest.annotator\n\nimport com.intellij.codeInsight.daemon.impl.HighlightRangeExtension\nimport com.intellij.lang.annotation.AnnotationHolder\nimport com.intellij.lang.annotation.Annotator\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.jetbrains.php.lang.psi.resolve.types.PhpParameterBasedTypeProvider\n\nclass PestAnnotator: Annotator, HighlightRangeExtension {\n    override fun annotate(element: PsiElement, holder: AnnotationHolder) {\n        if (PhpParameterBasedTypeProvider.isMeta(holder.currentAnnotationSession.file)) return\n        element.accept(PestAnnotatorVisitor(holder))\n    }\n\n    override fun isForceHighlightParents(psiFile: PsiFile): Boolean {\n        return false\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/annotator/PestAnnotatorVisitor.kt",
    "content": "package com.pestphp.pest.annotator\n\nimport com.intellij.lang.annotation.AnnotationHolder\nimport com.intellij.lang.annotation.HighlightSeverity\nimport com.intellij.modcommand.ModCommandAction\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.lang.annotator.PhpAnnotatorVisitor.createErrorAnnotation\nimport com.jetbrains.php.lang.annotator.PhpDeleteElementQuickFix\nimport com.jetbrains.php.lang.inspections.controlFlow.PhpNavigateToElementQuickFix\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.features.customExpectations.KEY\nimport com.pestphp.pest.features.customExpectations.extendName\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\n\nclass PestAnnotatorVisitor(\n    private val holder: AnnotationHolder\n) : PhpElementVisitor() {\n    override fun visitPhpMethodReference(reference: MethodReference) {\n        checkDuplicateCustomExpectations(reference)\n    }\n\n    private fun checkDuplicateCustomExpectations(reference: MethodReference) {\n        if (reference !is MethodReferenceImpl) {\n            return\n        }\n        val extendName = reference.extendName ?: return\n        val duplicates = mutableListOf<MethodReference>()\n        FileBasedIndex.getInstance().processValues(KEY, extendName, null, { file, offsets ->\n            reference.manager.findFile(file)?.let { psiFile ->\n                offsets.forEach { offset ->\n                    val expectation = psiFile.findElementAt(offset)\n                    val methodDescriptor = PhpPsiUtil.getParentOfClass(expectation, MethodReference::class.java)\n                    if (methodDescriptor != null) {\n                        duplicates.add(methodDescriptor)\n                    }\n                }\n            }\n            true\n        }, GlobalSearchScope.allScope(reference.project))\n        if (duplicates.size > 1) {\n            val fixes = listOfNotNull(\n                PhpDeleteElementQuickFix(reference.parent, PestBundle.message(\"QUICK_FIX_DELETE_CUSTOM_EXPECTATION\", extendName)),\n                getNavigateToCustomExpectationFix(duplicates, reference)\n            )\n            val builder = holder.newAnnotation(\n                HighlightSeverity.WARNING,\n                PestBundle.message(\"INSPECTION_DUPLICATE_CUSTOM_EXPECTATION\")\n            ).range(reference)\n            fixes.forEach { fix -> builder.withFix(fix.asIntention()) }\n            builder.create()\n        }\n    }\n\n    private fun getNavigateToCustomExpectationFix(\n        duplicates: List<MethodReference>,\n        duplicate: MethodReference\n    ): ModCommandAction? {\n        val duplicateIndex = duplicates.indexOf(duplicate)\n        if (duplicateIndex == -1) {\n            return null\n        }\n        val nextElement = duplicates[(duplicateIndex + 1) % duplicates.size]\n        return PhpNavigateToElementQuickFix(\n            nextElement,\n            PestBundle.message(\"INTENTION_NAVIGATE_TO_DUPLICATE_CUSTOM_EXPECTATION\")\n        )\n    }\n\n    override fun visitPhpFile(phpFile: PhpFile) {\n        checkDuplicateTestNames(phpFile)\n    }\n\n    private fun checkDuplicateTestNames(file: PhpFile) {\n        file.getPestTests(isSmart = true)\n            .groupBy { it.getPestTestName() }\n            .filterKeys { it != null }\n            .filter { it.value.count() > 1 }\n            .forEach { (_, tests) ->\n                tests.forEachIndexed { index, test ->\n                    val testName = test.getPestTestName() ?: return@forEachIndexed\n                    createErrorAnnotation(holder, test, PestBundle.message(\"INSPECTION_DUPLICATE_TEST_NAME\"),\n                                          PhpDeleteElementQuickFix(test.parent, PestBundle.message(\"QUICK_FIX_DELETE_TEST\", testName)),\n                                          getNavigateToTestNameFix(tests, index))\n                }\n            }\n    }\n\n    private fun getNavigateToTestNameFix(duplicates: List<FunctionReference>, duplicateIndex: Int): ModCommandAction {\n        val nextElement = duplicates[(duplicateIndex + 1) % duplicates.size]\n        return PhpNavigateToElementQuickFix(\n            nextElement,\n            PestBundle.message(\"INTENTION_NAVIGATE_TO_DUPLICATE_TEST_NAME\")\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/completion/InternalMembersCompletionProvider.kt",
    "content": "package com.pestphp.pest.completion\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.util.ProcessingContext\nimport com.jetbrains.php.PhpIndex\nimport com.jetbrains.php.completion.PhpVariantsUtil\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.Variable\nimport com.pestphp.pest.isAnyPestFunction\nimport com.pestphp.pest.isThisVariableInPest\n\n/**\n * Adds completion for private and protected methods of `$this` variable\n * when inside a pest test.\n */\nclass InternalMembersCompletionProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        val fieldReference = parameters.position.parent as? FieldReference ?: return\n\n        val variable = fieldReference.classReference as? Variable ?: return\n\n        if (!variable.isThisVariableInPest { it.isAnyPestFunction() }) return\n\n        val phpIndex = PhpIndex.getInstance(fieldReference.project)\n        val classes = phpIndex.completeType(fieldReference.project, variable.type, null).types\n            .filter { it.startsWith(\"\\\\\") }\n            .flatMap {\n                phpIndex.getAnyByFQN(it)\n            }\n\n        classes.flatMap { phpClass ->\n            phpClass.methods.filter { it.access.isProtected || (!it.access.isPrivate && it.isStatic) }\n        }.forEach {\n            result.addElement(PhpVariantsUtil.getLookupItem(it, null))\n        }\n\n        classes.flatMap { phpClass ->\n            phpClass.fields.filter { it.modifier.isProtected }\n        }.forEach {\n            result.addElement(PhpVariantsUtil.getLookupItem(it, null))\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/completion/PestCompletionContributor.kt",
    "content": "package com.pestphp.pest.completion\n\nimport com.intellij.codeInsight.completion.CompletionContributor\nimport com.intellij.codeInsight.completion.CompletionType\nimport com.intellij.patterns.PlatformPatterns\nimport com.jetbrains.php.lang.lexer.PhpTokenTypes\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.MemberReference\n\n/**\n * Registers the completion providers\n */\nprivate class PestCompletionContributor : CompletionContributor() {\n    init {\n        extend(\n            CompletionType.BASIC,\n            PlatformPatterns.psiElement()\n                .withElementType(PhpTokenTypes.IDENTIFIER)\n                .withParent(FieldReference::class.java),\n            InternalMembersCompletionProvider()\n        )\n\n        extend(\n            CompletionType.BASIC,\n            PlatformPatterns.psiElement()\n                .withElementType(PhpTokenTypes.IDENTIFIER)\n                .withParent(FieldReference::class.java),\n            ThisFieldsCompletionProvider()\n        )\n\n        extend(\n            CompletionType.BASIC,\n            PlatformPatterns.psiElement()\n                .withParent(MemberReference::class.java),\n            PestCustomExtensionCompletionProvider()\n        )\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/completion/PestCustomExtensionCompletionProvider.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.completion\n\nimport com.intellij.codeInsight.AutoPopupController\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.codeInsight.lookup.LookupElementPresentation\nimport com.intellij.codeInsight.lookup.LookupElementRenderer\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.ProcessingContext\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.PhpIcons\nimport com.jetbrains.php.completion.PhpCompletionUtil\nimport com.jetbrains.php.completion.insert.PhpInsertHandlerUtil\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.MemberReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.features.customExpectations.KEY\nimport com.pestphp.pest.features.customExpectations.expectationType\nimport com.pestphp.pest.features.customExpectations.toMethod\n\nclass PestCustomExtensionCompletionProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {\n        val memberReference = parameters.position.originalElement.parent as? MemberReference ?: return\n        val project = parameters.position.project\n        if (PhpType.intersectsGlobal(project, expectationType, memberReference.classReference?.globalType ?: PhpType.EMPTY)) {\n            val index = FileBasedIndex.getInstance()\n            index.getAllKeys(KEY, project).forEach { extensionName ->\n                index.processValues(KEY, extensionName, null, { file, value ->\n                    memberReference.manager.findFile(file)?.let { psiFile ->\n                        val cheapRenderer = CustomExpectationRenderer()\n                        val lookupElement = LookupElementBuilder.create(extensionName)\n                            .withRenderer(cheapRenderer)\n                            .withExpensiveRenderer(CustomExtensionExpensiveRenderer(cheapRenderer, psiFile, value.first()))\n                            .withInsertHandler { context, _ ->\n                                val expectation = psiFile.findElementAt(value.first())\n                                val methodDescriptor =\n                                    PhpPsiUtil.getParentOfClass(expectation, MethodReference::class.java)?.toMethod()\n                                PhpInsertHandlerUtil.insertStringAtCaret(context.editor, \"()\")\n                                if (methodDescriptor?.parameters?.isNotEmpty() == true) {\n                                    PhpCompletionUtil.moveCaretRelativelyWithScroll(context.editor, -1)\n                                    AutoPopupController.getInstance(project).autoPopupParameterInfo(context.editor, null)\n                                }\n                            }\n                        result.addElement(lookupElement)\n                    }\n                    true\n                }, GlobalSearchScope.projectScope(project))\n            }\n        }\n    }\n}\n\nprivate class CustomExpectationRenderer : LookupElementRenderer<LookupElement>() {\n    override fun renderElement(element: LookupElement, presentation: LookupElementPresentation) {\n        presentation.icon = PhpIcons.METHOD\n        presentation.itemText = element.lookupString\n        presentation.isItemTextBold = true\n    }\n}\n\nprivate class CustomExtensionExpensiveRenderer(\n    private val cheapRenderer: CustomExpectationRenderer,\n    private val targetFile: PsiFile,\n    private val expectationOffset: Int\n) : LookupElementRenderer<LookupElement>() {\n    override fun renderElement(element: LookupElement, presentation: LookupElementPresentation) {\n        cheapRenderer.renderElement(element, presentation)\n        val expectation = targetFile.findElementAt(expectationOffset)\n        PhpPsiUtil.getParentOfClass(expectation, MethodReference::class.java)?.toMethod()?.let {\n            val params = it.parameters.map { p ->\n                if (p.returnType.isEmpty) {\n                    p.name\n                } else {\n                    \"${p.name}: ${p.returnType}\"\n                }\n            }\n            presentation.tailText = \"(${params.joinToString(\", \")})\"\n            presentation.typeText = it.returnType.global(targetFile.project).toString()\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/completion/ThisFieldsCompletionProvider.kt",
    "content": "package com.pestphp.pest.completion\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.elementType\nimport com.intellij.util.ProcessingContext\nimport com.jetbrains.php.PhpIcons\nimport com.jetbrains.php.lang.lexer.PhpTokenTypes\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.Variable\nimport com.pestphp.pest.getAllBeforeThisAssignments\nimport com.pestphp.pest.isAnyPestFunction\nimport com.pestphp.pest.isThisVariableInPest\n\n/**\n * Adds completion for variable assignments from `beforeEach` when using `$this`\n * inside a pest test.\n */\ninternal class ThisFieldsCompletionProvider : CompletionProvider<CompletionParameters>(), GotoDeclarationHandler {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        val fieldReference = parameters.position.parent as? FieldReference ?: return\n\n        val variable = fieldReference.classReference as? Variable ?: return\n\n        if (!variable.isThisVariableInPest { it.isAnyPestFunction() }) return\n\n        return (fieldReference.containingFile).getAllBeforeThisAssignments()\n            .filter { it.variable?.name !== null }\n            .forEach {\n                result.addElement(\n                    LookupElementBuilder.create(it.variable!!.name!!)\n                        .withIcon(PhpIcons.FIELD)\n                        .withTypeText(it.type.toStringRelativized(\"\\\\\"))\n                )\n            }\n    }\n\n    override fun getGotoDeclarationTargets(\n        sourceElement: PsiElement?,\n        offset: Int,\n        editor: Editor?\n    ): Array<PsiElement> {\n        if (sourceElement?.elementType != PhpTokenTypes.IDENTIFIER) {\n            return PsiElement.EMPTY_ARRAY\n        }\n\n        val fieldReference = sourceElement?.parent as? FieldReference\n            ?: return PsiElement.EMPTY_ARRAY\n\n        if (fieldReference.classReference?.isThisVariableInPest { it.isAnyPestFunction() } != true) {\n            return PsiElement.EMPTY_ARRAY\n        }\n\n        return (fieldReference.containingFile ?: return PsiElement.EMPTY_ARRAY).getAllBeforeThisAssignments()\n            .filter { it.variable?.name == fieldReference.name }\n            .mapNotNull { it.variable }\n            .toTypedArray()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestDebugRunner.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.jetbrains.php.testFramework.run.PhpTestDebugRunner\n\n/**\n * Add support for Php's debug runners.\n */\nclass PestDebugRunner private constructor() :\n    PhpTestDebugRunner<PestRunConfiguration>(PestRunConfiguration::class.java) {\n    override fun getRunnerId(): String {\n        return \"PestDebugRunner\"\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestLocationProvider.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.Location\nimport com.intellij.execution.testframework.sm.runner.SMTestLocator\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.phpunit.PhpPsiLocationWithDataSet\nimport com.jetbrains.php.phpunit.PhpUnitQualifiedNameLocationProvider\nimport com.jetbrains.php.util.pathmapper.PhpLocalPathMapper\nimport com.jetbrains.php.util.pathmapper.PhpPathMapper\nimport com.pestphp.pest.features.parallel.convertRuntimeTestNameToRealTestName\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\nimport com.pestphp.pest.runner.LocationInfo\nimport java.io.File\n\nprivate const val PARALLEL_EXECUTION_URL_MARKER = \"eval()'d code::\"\n\n/**\n * Adds support for goto test from test results.\n */\nclass PestLocationProvider(\n    val pathMapper: PhpPathMapper,\n    private val project: Project,\n    private val configurationFileRootPath: String? = null\n) : SMTestLocator {\n    private val phpUnitLocationProvider = PhpUnitQualifiedNameLocationProvider.create(pathMapper)\n\n    override fun getLocation(\n        protocol: String,\n        path: String,\n        project: Project,\n        scope: GlobalSearchScope\n    ): MutableList<Location<PsiElement>> {\n        val isParallelExecution = path.contains(PARALLEL_EXECUTION_URL_MARKER)\n        if (protocol != PROTOCOL_ID && !isParallelExecution) {\n            return phpUnitLocationProvider.getLocation(protocol, path, project, scope)\n        }\n\n        val locationInfo = if (isParallelExecution) getParallelLocationInfo(path) else getLocationInfo(path)\n        val element = locationInfo?.let { findElement(it, project) } ?: return mutableListOf()\n\n        return mutableListOf(\n            PhpPsiLocationWithDataSet(\n                project,\n                element,\n                getDataSet(locationInfo)\n            )\n        )\n    }\n\n    private fun getDataSet(locationInfo: LocationInfo): String? {\n        return locationInfo.testName\n    }\n\n    private fun getLocationInfo(link: String): LocationInfo? {\n        val location = link.split(\"::\")\n        return resolveLocationInfo(location)\n    }\n\n    private fun getParallelLocationInfo(link: String): LocationInfo? {\n        val rawParallelLocation = link.substringAfter(PARALLEL_EXECUTION_URL_MARKER).split(\"::\")\n        val location = listOfNotNull(\n            convertLocationHintClassNameToFileName(rawParallelLocation[0]),\n            rawParallelLocation.getOrNull(1)?.let { runtimeTestName -> convertRuntimeTestNameToRealTestName(runtimeTestName) }\n        )\n        return resolveLocationInfo(location)\n    }\n\n    private fun resolveLocationInfo(location: List<String>): LocationInfo? {\n        val fileUrl = calculateFileUrl(location[0])\n        val testName = location.getOrNull(1)\n        val file = this.pathMapper.getLocalFile(fileUrl) ?: PhpLocalPathMapper(project).getLocalFile(fileUrl)\n        return file?.let { LocationInfo(it, testName) }\n    }\n\n    private fun findElement(locationInfo: LocationInfo, project: Project): PsiElement? {\n        return this.getLocation(\n            project,\n            locationInfo.file,\n            locationInfo.testName\n        )\n    }\n\n    private fun getLocation(project: Project, virtualFile: VirtualFile, testName: String?): PsiElement? {\n        val file = PsiManager.getInstance(project).findFile(virtualFile) ?: return null\n\n        if (testName == null) {\n            return file\n        }\n\n        return (file as PhpFile).getPestTests().firstOrNull { it.getPestTestName() == testName }\n    }\n\n    override fun getLocation(\n        stacktraceLine: String,\n        project: Project,\n        scope: GlobalSearchScope\n    ): MutableList<Location<PsiElement>> {\n        return mutableListOf()\n    }\n\n    fun calculateFileUrl(locationOutput: String): String {\n        val pathPrefix = configurationFileRootPath ?: project.basePath\n        return if (pathPrefix != null && locationOutput.startsWith(pathPrefix)) {\n            locationOutput // for Pest versions 1.x\n        } else {\n            \"$pathPrefix/${locationOutput}\" // for Pest versions >= 2.x\n        }\n    }\n\n    companion object {\n        const val PROTOCOL_ID = \"pest_qn\"\n    }\n}\n\nprivate fun convertLocationHintClassNameToFileName(locationHintClassName: String): String {\n    return locationHintClassName.removePrefix(\"\\\\P\\\\\").replace(\"\\\\\", File.separator) + \".php\"\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRerunFailedTestsAction.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.Executor\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.runners.ProgramRunner\nimport com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties\nimport com.intellij.openapi.ui.ComponentContainer\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.jetbrains.php.composer.configData.ComposerConfigManager\nimport com.jetbrains.php.config.commandLine.PhpCommandSettings\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.features.parallel.PestParallelProgramRunner\nimport com.pestphp.pest.isPestTestReference\nimport com.pestphp.pest.notifications.OutdatedNotification\nimport com.pestphp.pest.toPestTestRegex\n\n/**\n * Adds support for rerunning failed tests\n */\nclass PestRerunFailedTestsAction(\n    componentContainer: ComponentContainer,\n    properties: SMTRunnerConsoleProperties\n) : AbstractRerunFailedTestsAction(componentContainer) {\n    override fun getRunProfile(environment: ExecutionEnvironment): MyRunProfile? {\n        val profile = myConsoleProperties.configuration\n\n        if (profile !is PestRunConfiguration) {\n            return null\n        }\n\n        val runConfiguration: PestRunConfiguration = profile\n        return object : MyRunProfile(runConfiguration), PestRerunProfile {\n            override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState? {\n                val peerRunConfiguration = this.peer as PestRunConfiguration\n                val project = peerRunConfiguration.project\n                val interpreter = peerRunConfiguration.interpreter ?: return null\n\n                val failed = getFailedTests(project)\n                    .asSequence()\n                    .filter { it.isLeaf }\n                    .filter { it.parent != null }\n                    .map { it.getLocation(project, GlobalSearchScope.allScope(project)) }\n                    .mapNotNull { it?.psiElement }\n                    .filter { it.isPestTestReference() }\n                    .toList()\n\n                val clone: PestRunConfiguration = peerRunConfiguration.clone() as PestRunConfiguration\n\n                // If there are no failed tests found, it's prob.\n                // because it's an pest version before the new printer\n                if (failed.isEmpty()) {\n                    OutdatedNotification().notify(\n                        project,\n                        PestBundle.message(\"NO_FAILED_TESTS_FOUND\")\n                    )\n\n                    return peerRunConfiguration.getState(\n                        environment,\n                        clone.createCommand(\n                            interpreter,\n                            mutableMapOf(),\n                            mutableListOf(),\n                            false\n                        ),\n                        null\n                    )\n                }\n\n                val command: PhpCommandSettings = clone.createCommand(\n                    interpreter,\n                    mutableMapOf(),\n                    getArgumentsFromRunner(environment.runner),\n                    false\n                )\n\n                val rootPath = ComposerConfigManager.getInstance(project).getConfig(null as PsiElement?)?.parent?.path ?: command.workingDirectory\n\n                val testcases = failed.mapNotNull { it.toPestTestRegex(rootPath) }\n                    .reduce { result, testName -> \"$result|$testName\" }\n\n                command.addArgument(\n                    \"--filter=/$testcases/\"\n                )\n\n                return peerRunConfiguration.getState(\n                    environment,\n                    command,\n                    null\n                )\n            }\n        }\n    }\n\n    init {\n        init(properties)\n    }\n\n    private fun getArgumentsFromRunner(pestProgramRunner: ProgramRunner<*>): MutableList<String> {\n        return when (pestProgramRunner) {\n            is PestParallelProgramRunner -> pestProgramRunner.getArguments()\n            else -> mutableListOf()\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRerunProfile.kt",
    "content": "package com.pestphp.pest.configuration\n\ninterface PestRerunProfile"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRunConfiguration.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.execution.ExecutionException\nimport com.intellij.execution.Executor\nimport com.intellij.execution.configurations.ConfigurationFactory\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.configurations.RuntimeConfigurationException\nimport com.intellij.execution.configurations.RuntimeConfigurationWarning\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.options.SettingsEditor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.util.PathUtil\nimport com.intellij.util.TextFieldCompletionProvider\nimport com.jetbrains.php.PhpBundle\nimport com.jetbrains.php.config.commandLine.PhpCommandLinePathProcessor\nimport com.jetbrains.php.config.commandLine.PhpCommandSettings\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.run.PhpAsyncRunConfiguration\nimport com.jetbrains.php.run.PhpRunUtil\nimport com.jetbrains.php.run.remote.PhpRemoteInterpreterManager\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.jetbrains.php.testFramework.run.PhpTestRunConfigurationSettings\nimport com.jetbrains.php.testFramework.run.PhpTestRunnerConfigurationEditor\nimport com.jetbrains.php.testFramework.run.PhpTestRunnerSettings\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestFrameworkType\nimport com.pestphp.pest.PestIcons\nimport com.pestphp.pest.configuration.PestRunConfigurationProducer.Companion.VALIDATOR\nimport com.pestphp.pest.features.parallel.addParallelArguments\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\nimport com.pestphp.pest.runner.PestConsoleProperties\nimport java.util.EnumMap\nimport kotlin.io.path.Path\n\nclass PestRunConfiguration(project: Project, factory: ConfigurationFactory) : PhpTestRunConfiguration(\n    project,\n    factory,\n    PestBundle.message(\"FRAMEWORK_NAME\"),\n    PestFrameworkType.instance,\n    VALIDATOR,\n    PestRunConfigurationHandler.instance,\n    PestVersionDetector.instance\n), PhpAsyncRunConfiguration {\n    override fun createSettings(): PestRunConfigurationSettings {\n        return PestRunConfigurationSettings()\n    }\n\n    override fun getConfigurationEditor(): SettingsEditor<out RunConfiguration?> {\n        val names = EnumMap<PhpTestRunnerSettings.Scope, String>(PhpTestRunnerSettings.Scope::class.java)\n        val editor = this.getConfigurationEditor(names)\n\n        editor.setRunnerOptionsDocumentation(\"https://pestphp.com/docs/installation\")\n        return PestTestRunConfigurationEditor(editor, this)\n    }\n\n    @Throws(ExecutionException::class)\n    @Suppress(\"SwallowedException\")\n    fun checkAndGetState(env: ExecutionEnvironment, command: PhpCommandSettings): RunProfileState? {\n        try {\n            checkConfiguration()\n        } catch (ignored: RuntimeConfigurationWarning) {\n        } catch (exception: RuntimeConfigurationException) {\n            throw ExecutionException(PestBundle.message(\"RUNTIME_CONFIGURATION_EXCEPTION_MESSAGE\", exception.localizedMessage, this.name))\n        }\n        return this.getState(env, command, null)\n    }\n\n    override fun createMethodFieldCompletionProvider(\n        editor: PhpTestRunnerConfigurationEditor\n    ): TextFieldCompletionProvider {\n        return object : TextFieldCompletionProvider() {\n            override fun addCompletionVariants(text: String, offset: Int, prefix: String, result: CompletionResultSet) {\n                val file = PhpRunUtil.findPsiFile(project, settings.runnerSettings.filePath)\n                file?.getPestTests()\n                    ?.mapNotNull { it.getPestTestName() }\n                    ?.map { LookupElementBuilder.create(it).withIcon(PestIcons.File) }\n                    ?.forEach { result.addElement(it) }\n            }\n        }\n    }\n\n    override fun createRerunAction(\n        consoleView: ConsoleView,\n        properties: SMTRunnerConsoleProperties\n    ): AbstractRerunFailedTestsAction {\n        return PestRerunFailedTestsAction(consoleView, properties)\n    }\n\n    override fun createTestConsoleProperties(executor: Executor): SMTRunnerConsoleProperties {\n        val manager = PhpRemoteInterpreterManager.getInstance()\n\n\n        val pathProcessor = when (this.interpreter?.isRemote) {\n            true -> manager?.createPathMapper(this.project, interpreter!!.phpSdkAdditionalData)\n            else -> null\n        }\n\n        return this.createTestConsoleProperties(\n            executor,\n            pathProcessor ?: PhpCommandLinePathProcessor.LOCAL\n        )\n    }\n\n    private fun createTestConsoleProperties(\n        executor: Executor,\n        processor: PhpCommandLinePathProcessor\n    ): PestConsoleProperties {\n        val pathMapper = processor.createPathMapper(this.project)\n        return PestConsoleProperties(\n            this,\n            executor,\n            PestLocationProvider(pathMapper, this.project, this.getConfigurationFileRootPath())\n        )\n    }\n\n    override fun suggestedName(): String? {\n        val runner = this.settings.runnerSettings\n        return when (val scope = runner.scope) {\n            PhpTestRunnerSettings.Scope.Directory -> PathUtil.getFileName(StringUtil.notNullize(runner.directoryPath))\n            PhpTestRunnerSettings.Scope.File -> PathUtil.getFileName(StringUtil.notNullize(runner.filePath))\n            PhpTestRunnerSettings.Scope.Method -> {\n                val file = PathUtil.getFileName(StringUtil.notNullize(runner.filePath))\n                \"$file::${runner.methodName}\"\n            }\n            PhpTestRunnerSettings.Scope.ConfigurationFile -> PathUtil.getFileName(\n                StringUtil.notNullize(runner.configurationFilePath)\n            )\n            else -> {\n                assert(false) { \"Unknown scope: $scope\" }\n                null\n            }\n        }\n    }\n\n    fun applyTestArguments(command: PhpCommandSettings, coverageArguments: List<String>) {\n        val config = PhpTestFrameworkSettingsManager.getInstance(project)\n            .getOrCreateByInterpreter(PestFrameworkType.instance, interpreter, true)\n            ?: throw ExecutionException(PestBundle.message(\"DIALOG_MESSAGE_COULD_NOT_FIND_PHP_INTERPRETER\"))\n\n        val version = null\n\n        val workingDirectory = getWorkingDirectory(project, settings, config)\n            ?: throw ExecutionException(PhpBundle.message(\"php.interpreter.base.configuration.working.directory\"))\n\n        PestRunConfigurationHandler.instance.prepareCommand(\n            project,\n            command,\n            config.executablePath!!,\n            version\n        )\n\n        command.importCommandLineSettings(settings.commandLineSettings, workingDirectory)\n        fillTestRunnerArguments(\n            project,\n            workingDirectory,\n            settings.runnerSettings,\n            coverageArguments,\n            command,\n            config,\n            PestRunConfigurationHandler.instance\n        )\n    }\n\n    override fun createCommand(\n        interpreter: PhpInterpreter,\n        env: MutableMap<String, String>,\n        arguments: MutableList<String>,\n        withDebugger: Boolean\n    ): PhpCommandSettings {\n        PestRunConfigurationHandler.instance.rootPath = getConfigurationFileRootPath()\n        return super.createCommand(interpreter, env, arguments, withDebugger).apply {\n            addParallelArguments(this@PestRunConfiguration, this)\n        }\n    }\n\n    override fun getWorkingDirectory(\n        project: Project,\n        settings: PhpTestRunConfigurationSettings,\n        config: PhpTestFrameworkConfiguration?\n    ): String? {\n        val cli = settings.commandLineSettings\n        if (cli.workingDirectory?.isNotEmpty() == true) {\n            return cli.workingDirectory\n        }\n\n        val configFileRootPath = getConfigurationFileRootPath()\n\n        if (configFileRootPath.isNullOrEmpty()) {\n            return super.getWorkingDirectory(project, settings, config)\n        }\n\n        return configFileRootPath\n    }\n\n    private fun getConfigurationFileRootPath(): String? {\n        val configFile = getConfigurationFile(\n            settings.runnerSettings,\n            PhpTestFrameworkSettingsManager.getInstance(project)\n                .getOrCreateByInterpreter(PestFrameworkType.instance, interpreter, true)\n        ) ?: return null\n\n        return VfsUtil.findFile(Path(configFile), false)?.parent?.path\n    }\n\n    val pestSettings: PestRunConfigurationSettings\n        get() {\n            return super.getSettings() as PestRunConfigurationSettings\n        }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRunConfigurationHandler.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.ExecutionException\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.php.config.commandLine.PhpCommandSettings\nimport com.jetbrains.php.testFramework.run.PhpTestRunConfigurationHandler\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.toPestTestRegex\n\nclass PestRunConfigurationHandler : PhpTestRunConfigurationHandler {\n    var rootPath: String? = null\n\n    companion object {\n        @JvmField\n        val instance = PestRunConfigurationHandler()\n    }\n\n    override fun getConfigFileOption(): String {\n        return \"--configuration\"\n    }\n\n    override fun prepareCommand(project: Project, commandSettings: PhpCommandSettings, exe: String, version: String?) {\n        commandSettings.setScript(exe, false)\n        commandSettings.addArgument(\"--teamcity\")\n        commandSettings.addEnv(\"IDE_PEST_EXE\", exe)\n\n        if (!version.isNullOrEmpty()) {\n            commandSettings.addEnv(\"IDE_PEST_VERSION\", version)\n        }\n    }\n\n    @Throws(ExecutionException::class)\n    override fun runType(\n        project: Project,\n        phpCommandSettings: PhpCommandSettings,\n        type: String,\n        workingDirectory: String\n    ) {\n        throw ExecutionException(PestBundle.message(\"CANNOT_RUN_PEST_WITH_TYPE_MESSAGE\"))\n    }\n\n    override fun runDirectory(\n        project: Project,\n        phpCommandSettings: PhpCommandSettings,\n        directory: String,\n        workingDirectory: String\n    ) {\n        if (directory.isEmpty()) {\n            return\n        }\n        phpCommandSettings.addPathArgument(directory)\n    }\n\n    override fun runFile(\n        project: Project,\n        phpCommandSettings: PhpCommandSettings,\n        file: String,\n        workingDirectory: String\n    ) {\n        if (file.isEmpty()) {\n            return\n        }\n        phpCommandSettings.addPathArgument(file)\n    }\n\n    override fun runMethod(\n        project: Project,\n        phpCommandSettings: PhpCommandSettings,\n        file: String,\n        methodName: String,\n        workingDirectory: String\n    ) {\n        if (file.isEmpty()) {\n            return\n        }\n        val pathMapper = phpCommandSettings.pathProcessor.createPathMapper(project)\n\n        val rootPath = this.rootPath ?: workingDirectory\n\n        phpCommandSettings.addPathArgument(file)\n        phpCommandSettings.addArgument(\"--filter\")\n        phpCommandSettings.addArgument(\"/${methodName.toPestTestRegex(rootPath, file, pathMapper)}/\")\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRunConfigurationProducer.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.actions.ConfigurationFromContext\nimport com.intellij.execution.configurations.ConfigurationFactory\nimport com.intellij.ide.highlighter.XmlFileType\nimport com.intellij.openapi.fileTypes.FileType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Condition\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiDirectory\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.util.Function\nimport com.jetbrains.php.lang.PhpFileType\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.testFramework.run.PhpDefaultTestRunnerSettingsValidator\nimport com.jetbrains.php.testFramework.run.PhpTestConfigurationProducer\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isPestConfigurationFile\nimport com.pestphp.pest.isPestEnabled\nimport com.pestphp.pest.isPestTestFile\nimport com.pestphp.pest.isPestTestReference\n\nclass PestRunConfigurationProducer : PhpTestConfigurationProducer<PestRunConfiguration>(\n    VALIDATOR,\n    FILE_TO_SCOPE,\n    METHOD_NAMER,\n    METHOD\n) {\n    override fun getConfigurationFactory(): ConfigurationFactory = PestRunConfigurationType.instance\n\n    override fun isEnabled(project: Project): Boolean = project.isPestEnabled()\n\n    override fun getWorkingDirectory(element: PsiElement): VirtualFile? {\n        if (element is PsiDirectory) {\n            return element.parentDirectory?.virtualFile\n        }\n\n        return element.containingFile?.containingDirectory?.virtualFile\n    }\n\n    companion object {\n        val METHOD = Condition<PsiElement> { element: PsiElement? ->\n            element.isPestTestReference()\n        }\n        private val METHOD_NAMER = Function<PsiElement, String?> { element: PsiElement? ->\n            element.getPestTestName()\n        }\n        private val FILE_TO_SCOPE = Function<PsiFile, PsiElement?> { file: PsiFile ->\n            if (file.isPestTestFile()) file else null\n        }\n        val VALIDATOR = PhpDefaultTestRunnerSettingsValidator(\n            setOf<FileType>(PhpFileType.INSTANCE, XmlFileType.INSTANCE).toList(),\n            { file: PsiFile, _: String ->\n                file.isPestConfigurationFile() || file.isPestTestFile()\n            },\n            false,\n            false\n        )\n    }\n\n    override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {\n        val file = self.sourceElement as? PhpFile ?: return false\n        return file.isPestTestFile()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRunConfigurationType.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.configurations.ConfigurationTypeUtil.findConfigurationType\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.SimpleConfigurationType\nimport com.intellij.openapi.project.DumbAware\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.NotNullLazyValue\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestIcons\n\nclass PestRunConfigurationType private constructor() :\n    SimpleConfigurationType(\n        \"PestRunConfigurationType\",\n        PestBundle.message(\"FRAMEWORK_NAME\"),\n        PestBundle.message(\"FRAMEWORK_NAME\"),\n        NotNullLazyValue.createValue { PestIcons.Config }\n    ),\n    DumbAware {\n    override fun createTemplateConfiguration(project: Project): RunConfiguration {\n        return PestRunConfiguration(project, this)\n    }\n\n    companion object {\n        @JvmStatic\n        val instance: PestRunConfigurationType\n            get() = findConfigurationType(PestRunConfigurationType::class.java)\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestRunnerSettings.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.util.xmlb.annotations.Attribute\nimport com.intellij.util.xmlb.annotations.Tag\nimport com.jetbrains.php.phpunit.coverage.PhpUnitCoverageEngine.CoverageEngine\nimport com.jetbrains.php.testFramework.run.PhpTestRunnerSettings\n\n@Tag(\"PestRunner\")\nclass PestRunnerSettings : PhpTestRunnerSettings() {\n    @Attribute(\"coverage_engine\")\n    var coverageEngine: CoverageEngine = CoverageEngine.XDEBUG\n    @Attribute(\"parallel_testing_enabled\")\n    var parallelTestingEnabled: Boolean = false\n\n    companion object {\n        @JvmStatic\n        fun fromPhpTestRunnerSettings(settings: PhpTestRunnerSettings): PestRunnerSettings {\n            val pestSettings = PestRunnerSettings()\n\n            pestSettings.scope = settings.scope\n            pestSettings.selectedType = settings.selectedType\n            pestSettings.directoryPath = settings.directoryPath\n            pestSettings.filePath = settings.filePath\n            pestSettings.methodName = settings.methodName\n            pestSettings.isUseAlternativeConfigurationFile = settings.isUseAlternativeConfigurationFile\n            pestSettings.configurationFilePath = settings.configurationFilePath\n            pestSettings.testRunnerOptions = settings.testRunnerOptions\n\n            return pestSettings\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (other !is PestRunnerSettings) return false\n        return super.equals(other) && coverageEngine == other.coverageEngine && parallelTestingEnabled == other.parallelTestingEnabled\n    }\n\n    override fun hashCode(): Int {\n        var result = super.hashCode()\n        result = 31 * result + coverageEngine.hashCode()\n        result = 31 * result + parallelTestingEnabled.hashCode()\n        return result\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestTestRunConfigurationEditor.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.openapi.editor.ReadOnlyModificationException\nimport com.intellij.openapi.options.SettingsEditor\nimport com.intellij.openapi.ui.ComboBox\nimport com.intellij.ui.components.JBCheckBox\nimport com.intellij.util.ui.UI\nimport com.jetbrains.php.phpunit.coverage.PhpUnitCoverageEngine.CoverageEngine\nimport com.jetbrains.php.testFramework.run.PhpTestRunConfigurationEditor\nimport com.pestphp.pest.PestBundle\nimport java.lang.reflect.InvocationTargetException\nimport javax.swing.BoxLayout\nimport javax.swing.JComponent\nimport javax.swing.JPanel\n\nclass PestTestRunConfigurationEditor(\n    private val parentEditor: PhpTestRunConfigurationEditor,\n    settings: PestRunConfiguration\n) : SettingsEditor<PestRunConfiguration>() {\n    private val myMainPanel = JPanel()\n    private var coveragePanel = JPanel()\n    private var parallelPanel = JPanel()\n    private val coverageEngineComboBox = ComboBox(arrayOf(CoverageEngine.XDEBUG, CoverageEngine.PCOV))\n    private val enabledParallelTestingCheckBox = JBCheckBox()\n\n    init {\n        coveragePanel = UI.PanelFactory.grid().add(\n            UI.PanelFactory.panel(coverageEngineComboBox).withLabel(PestBundle.message(\"COVERAGE_ENGINE_LABEL_TEXT\"))\n        ).createPanel()\n        parallelPanel = UI.PanelFactory.grid().add(\n            UI.PanelFactory.panel(enabledParallelTestingCheckBox).withLabel(PestBundle.message(\"ENABLE_PARALLEL_TESTING_LABEL_TEXT\"))\n        ).createPanel()\n\n        myMainPanel.layout = BoxLayout(myMainPanel, BoxLayout.Y_AXIS)\n        myMainPanel.add(parentEditor.component)\n        myMainPanel.add(coveragePanel)\n        myMainPanel.add(parallelPanel)\n        resetEditorFrom(settings)\n    }\n\n    override fun createEditor(): JComponent {\n        return myMainPanel\n    }\n\n    private fun doApply(configuration: PestRunConfiguration) {\n        val settings = configuration.settings as PestRunConfigurationSettings\n        val runnerSettings = settings.pestRunnerSettings\n\n        runnerSettings.coverageEngine = coverageEngineComboBox.selectedItem as CoverageEngine\n        runnerSettings.parallelTestingEnabled = enabledParallelTestingCheckBox.isSelected\n    }\n\n    private fun doReset(configuration: PestRunConfiguration) {\n        val settings = configuration.settings as PestRunConfigurationSettings\n        val runnerSettings = settings.pestRunnerSettings\n\n        coverageEngineComboBox.selectedItem = runnerSettings.coverageEngine\n        enabledParallelTestingCheckBox.isSelected = runnerSettings.parallelTestingEnabled\n    }\n\n    override fun resetEditorFrom(settings: PestRunConfiguration) {\n        doReset(settings)\n        parentEditor.javaClass.declaredMethods.find { it.name == \"resetEditorFrom\" }!!.let {\n            it.isAccessible = true\n            it.invoke(parentEditor, settings)\n        }\n    }\n\n    override fun applyEditorTo(settings: PestRunConfiguration) {\n        parentEditor.javaClass.declaredMethods.find { it.name == \"applyEditorTo\" }!!.let {\n            it.isAccessible = true\n            try {\n                it.invoke(parentEditor, settings)\n            } catch (exception: InvocationTargetException) {\n                // In case the method throws a read only error (happens in code with me) we ignore it.\n                if (exception.cause is ReadOnlyModificationException) {\n                    return@let\n                }\n\n                throw exception\n            }\n        }\n        doApply(settings)\n    }\n\n    override fun getSnapshot(): PestRunConfiguration {\n        val result = parentEditor.snapshot as PestRunConfiguration\n        doApply(result)\n        return result\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/configuration/PestVersionDetector.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.ExecutionException\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.php.PhpTestFrameworkVersionDetector\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.pestphp.pest.PestBundle\nimport org.jetbrains.annotations.Nls\n\nprivate val VERSION_REGEX = Regex(\"(?<major>\\\\d+)\\\\.(?<minor>\\\\d+)\\\\.(?<patch>\\\\d+)\")\nprivate val VERSION_OPTIONS = arrayOf(\"--version\", \"--colors=never\")\n\nclass PestVersionDetector : PhpTestFrameworkVersionDetector<String>() {\n    override fun getPresentableName(): @Nls String {\n        return PestBundle.message(\"FRAMEWORK_NAME\")\n    }\n\n    override fun getTitle(): String {\n        return PestBundle.message(\"GETTING_PEST_VERSION\")\n    }\n\n    override fun getVersionOptions(): Array<String> {\n        return VERSION_OPTIONS\n    }\n\n    public override fun parse(s: String): String {\n        val version = if (s.startsWith(\"Pest\")) {\n            // for <2.0.0 versions\n            s.removePrefix(\"Pest\").substringBefore(\"\\n\").trim()\n        } else {\n            // for 2.* versions\n            s.trim().removePrefix(\"Pest Testing Framework \").substringBeforeLast('.')\n        }\n\n        if (!version.matches(VERSION_REGEX)) {\n            throw ExecutionException(PestBundle.message(\"PEST_CONFIGURATION_UI_CAN_NOT_PARSE_VERSION\", s))\n        }\n        return version\n    }\n\n    override fun getVersion(project: Project, interpreter: PhpInterpreter, executable: String?): String {\n        if (interpreter.isRemote) {\n            throw ExecutionException(PestBundle.message(\"PEST_VERSION_IS_NOT_SUPPORTED_FOR_REMOTE_INTERPRETER\"))\n        }\n        return super.getVersion(project, interpreter, executable)\n    }\n\n    companion object {\n        val instance = PestVersionDetector()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/configuration/ConfigurationInDirectoryReferenceProvider.kt",
    "content": "package com.pestphp.pest.features.configuration\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiReference\nimport com.intellij.psi.PsiReferenceProvider\nimport com.intellij.util.ProcessingContext\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.pestphp.pest.CONFIGURATION_FUNCTIONS\n\nclass ConfigurationInDirectoryReferenceProvider: PsiReferenceProvider() {\n    override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {\n        val inCall = element.parent.parent as MethodReferenceImpl\n\n        if (inCall.canonicalText != \"in\") {\n            return PsiReference.EMPTY_ARRAY\n        }\n\n        val usesCall = getConfigurationFunctionCall(inCall) as? FunctionReferenceImpl ?: return PsiReference.EMPTY_ARRAY\n        if (usesCall.canonicalText !in CONFIGURATION_FUNCTIONS) {\n            return PsiReference.EMPTY_ARRAY\n        }\n\n        val referenceSet = PhpFolderReferenceSet(element, element as StringLiteralExpression, this)\n\n        return referenceSet\n            .allReferences\n            .toList()\n            .toTypedArray()\n    }\n}\n\nfun getConfigurationFunctionCall(inCall: MethodReference): FunctionReference? {\n    val child = inCall.firstPsiChild\n    if (child !is MethodReference) {\n        if (child is FunctionReference) {\n            return child\n        }\n        return null\n    }\n    return getConfigurationFunctionCall(child)\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/configuration/ConfigurationReferenceContributor.kt",
    "content": "package com.pestphp.pest.features.configuration\n\nimport com.intellij.patterns.PlatformPatterns\nimport com.intellij.psi.PsiReferenceContributor\nimport com.intellij.psi.PsiReferenceRegistrar\nimport com.jetbrains.php.lang.psi.elements.ParameterList\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\n\nclass ConfigurationReferenceContributor : PsiReferenceContributor() {\n    override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {\n        registrar.registerReferenceProvider(\n            PlatformPatterns.psiElement(StringLiteralExpression::class.java)\n                .withParents(\n                    ParameterList::class.java,\n                    MethodReferenceImpl::class.java\n                ),\n            ConfigurationInDirectoryReferenceProvider()\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/configuration/PhpFolderReferenceSet.kt",
    "content": "package com.pestphp.pest.features.configuration\n\nimport com.intellij.openapi.util.Condition\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFileSystemItem\nimport com.intellij.psi.PsiReferenceProvider\nimport com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.impl.PhpFileReferenceSet\n\nclass PhpFolderReferenceSet(element: PsiElement, argument: PhpPsiElement, provider: PsiReferenceProvider) : PhpFileReferenceSet(element, argument, provider) {\n    override fun getReferenceCompletionFilter(): Condition<PsiFileSystemItem> {\n        return FileReferenceSet.DIRECTORY_FILTER\n    }\n\n    override fun computeDefaultContexts(): MutableCollection<PsiFileSystemItem> {\n        val containingFile = this.element.containingFile.originalFile\n        val directory = containingFile.virtualFile.parent\n\n        val fileSystemItems = toFileSystemItems(directory)\n\n        if (fileSystemItems.isNotEmpty()) {\n            return fileSystemItems\n        }\n\n        return super.computeDefaultContexts()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/CustomExpectationIndex.kt",
    "content": "package com.pestphp.pest.features.customExpectations\n\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.indexing.DataIndexer\nimport com.intellij.util.indexing.DefaultFileTypeSpecificInputFilter\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.intellij.util.indexing.FileBasedIndexExtension\nimport com.intellij.util.indexing.FileContent\nimport com.intellij.util.indexing.ID\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.intellij.util.io.KeyDescriptor\nimport com.jetbrains.php.lang.PhpFileType\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.jetbrains.php.lang.psi.stubs.indexes.PhpDepthLimitedRecursiveElementVisitor\nimport com.jetbrains.php.lang.psi.stubs.indexes.PhpInvokeCallsOffsetsIndex.IntArrayExternalizer\nimport it.unimi.dsi.fastutil.ints.IntArrayList\nimport it.unimi.dsi.fastutil.ints.IntList\n\nval KEY = ID.create<String, IntList>(\"php.pest.custom_expectations\")\n\nclass CustomExpectationIndex : FileBasedIndexExtension<String, IntList>() {\n\n    override fun getName(): ID<String, IntList> {\n        return KEY\n    }\n\n    override fun getVersion(): Int {\n        return 7\n    }\n\n    override fun getIndexer(): DataIndexer<String, IntList, FileContent> {\n        return DataIndexer { inputData ->\n            val file = inputData.psiFile\n            val map: MutableMap<String, IntList> = mutableMapOf()\n            if (file is PhpFile) {\n                file.accept(object : PhpDepthLimitedRecursiveElementVisitor() {\n                    override fun visitPhpMethodReference(reference: MethodReference) {\n                        if (reference is MethodReferenceImpl && reference.isPestExtendReference()) {\n                            reference.extendName?.let {\n                                if (it !in map) {\n                                    map[it] = IntArrayList()\n                                }\n                                map[it]!!.add(reference.parameters[0].textOffset + 1)\n                            }\n                        }\n                    }\n                })\n            }\n            return@DataIndexer map\n        }\n    }\n\n    override fun getKeyDescriptor(): KeyDescriptor<String> {\n        return EnumeratorStringDescriptor.INSTANCE\n    }\n\n    override fun getValueExternalizer(): DataExternalizer<IntList> {\n        return IntArrayExternalizer.INSTANCE\n    }\n\n    override fun getInputFilter(): FileBasedIndex.InputFilter {\n        return object : DefaultFileTypeSpecificInputFilter(PhpFileType.INSTANCE) {\n            override fun acceptInput(file: VirtualFile): Boolean {\n                return !isPestStubFile(file)\n            }\n        }\n    }\n\n    override fun dependsOnFileContent(): Boolean {\n        return true\n    }\n\n    private fun isPestStubFile(file: VirtualFile): Boolean {\n        val path = file.path\n        return path.contains(\"vendor\") && path.contains(\"pestphp\") && path.contains(\"stubs\")\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/CustomExpectationNotifier.kt",
    "content": "package com.pestphp.pest.features.customExpectations\n\nimport com.intellij.psi.PsiFile\nimport com.intellij.util.messages.Topic\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport java.util.EventListener\n\ninterface CustomExpectationNotifier : EventListener {\n    companion object {\n        @Topic.ProjectLevel\n        val TOPIC = Topic.create(\"Custom expectation\", CustomExpectationNotifier::class.java)\n    }\n\n    fun changedExpectation(file: PsiFile, customExpectations: List<Method>)\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/CustomExpectationParameterInfoHandler.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations\n\nimport com.intellij.lang.parameterInfo.CreateParameterInfoContext\nimport com.intellij.lang.parameterInfo.ParameterInfoHandlerWithTabActionSupport\nimport com.intellij.lang.parameterInfo.ParameterInfoUIContext\nimport com.intellij.lang.parameterInfo.UpdateParameterInfoContext\nimport com.intellij.model.psi.PsiSymbolReferenceService\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.tree.IElementType\nimport com.intellij.util.IntPair\nimport com.intellij.util.containers.ContainerUtil\nimport com.jetbrains.php.lang.PhpParameterInfoHandler\nimport com.jetbrains.php.lang.lexer.PhpTokenTypes\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.pestphp.pest.features.customExpectations.generators.Parameter\nimport com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationReference\nimport com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationSymbol\n\nclass CustomExpectationParameterInfoHandler : ParameterInfoHandlerWithTabActionSupport<FunctionReference, List<Parameter>, PsiElement> {\n    override fun findElementForParameterInfo(context: CreateParameterInfoContext): FunctionReference? {\n        val methodReference =\n            PhpParameterInfoHandler.findAnchorForParameterInfo(context) as? MethodReferenceImpl ?: return null\n        val references = PsiSymbolReferenceService.getService().getReferences(methodReference)\n        val symbol = references.filterIsInstance<PestCustomExpectationReference>().flatMap { it.resolveReference() }\n            .filterIsInstance<PestCustomExpectationSymbol>().firstOrNull() ?: return null\n        context.itemsToShow = arrayOf(symbol.methodDescriptor.parameters)\n        return methodReference\n    }\n\n    override fun showParameterInfo(element: FunctionReference, context: CreateParameterInfoContext) {\n        context.showHint(element, element.textRange.startOffset, this)\n    }\n\n    override fun findElementForUpdatingParameterInfo(context: UpdateParameterInfoContext): FunctionReference? {\n        return PhpParameterInfoHandler.findAnchorForParameterInfo(context) as? FunctionReference\n    }\n\n    override fun updateParameterInfo(reference: FunctionReference, context: UpdateParameterInfoContext) {\n        context.setCurrentParameter(\n            PhpParameterInfoHandler.getCurrentParameterIndex(\n                reference, PhpParameterInfoHandler.getCurrentOffset(context),\n                actualParameterDelimiterType\n            )\n        )\n    }\n\n    override fun updateUI(params: List<Parameter>, context: ParameterInfoUIContext) {\n        if (params.isEmpty()) {\n            context.isUIComponentEnabled = false\n            return\n        }\n        var currentParameter = context.currentParameterIndex\n        if (currentParameter < 0) currentParameter = 0\n        val buffer = StringBuilder()\n        val highlightRange =\n            PhpParameterInfoHandler.appendParameterInfo(context, buffer, IntPair(-1, -1), currentParameter, params) { p ->\n                if (p.returnType.isEmpty) {\n                    p.name\n                }\n                else {\n                    \"${p.name}: ${p.returnType}\"\n                }\n            }\n        context.setupUIComponentPresentation(\n            buffer.toString(),\n            highlightRange.first,\n            highlightRange.second,\n            false,\n            false,\n            false,\n            context.defaultParameterColor\n        )\n    }\n\n    override fun getActualParameters(o: FunctionReference): Array<PsiElement> {\n        val parameters = o.parameters\n        return parameters.copyOf()\n    }\n\n    override fun getActualParameterDelimiterType(): IElementType {\n        return PhpTokenTypes.opCOMMA\n    }\n\n    override fun getActualParametersRBraceType(): IElementType {\n        return PhpTokenTypes.chRPAREN\n    }\n\n    override fun getArgumentListAllowedParentClasses(): Set<Class<*>> {\n        return ContainerUtil.newHashSet<Class<*>>(PhpPsiElement::class.java)\n    }\n\n    override fun getArgListStopSearchClasses(): Set<Class<*>> {\n        return ContainerUtil.newHashSet(\n            Statement::class.java\n        )\n    }\n\n    override fun getArgumentListClass(): Class<FunctionReference> {\n        return FunctionReference::class.java\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/CustomExpectationRemoveGeneratedFileStartupActivity.kt",
    "content": "package com.pestphp.pest.features.customExpectations\n\nimport com.intellij.openapi.application.runWriteAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.startup.ProjectActivity\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.jetbrains.php.composer.ComposerConfigListener\nimport com.jetbrains.php.composer.ComposerDataService\nimport com.jetbrains.php.composer.lib.ComposerLibraryServiceFactory\nimport com.pestphp.pest.PestPluginDisposable\n\nclass CustomExpectationRemoveGeneratedFileStartupActivity : ProjectActivity {\n    override suspend fun execute(project: Project) {\n        tryDeleteGeneratedExpectationFile(project)\n        val composerDataService = ComposerDataService.getInstance(project)\n        val listener = object : ComposerConfigListener {\n            override fun configPathChanged(oldPath: String?, newPath: String?, isWellConfigured: Boolean) {\n                if (newPath != null) {\n                    tryDeleteGeneratedExpectationFile(project)\n                }\n            }\n        }\n        composerDataService.addConfigListener(listener)\n        Disposer.register(PestPluginDisposable.getInstance(project)) {\n            composerDataService.removeConfigListener(listener)\n        }\n    }\n\n    private fun tryDeleteGeneratedExpectationFile(project: Project) {\n        ComposerLibraryServiceFactory.getInstance(project, null as VirtualFile?).vendorDir?.findChild(\"Expectation.php\")?.let {\n            runWriteAction { it.delete(null) }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/ListMethodDataExternalizer.kt",
    "content": "package com.pestphp.pest.features.customExpectations\n\nimport com.intellij.util.io.DataExternalizer\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass ListMethodDataExternalizer : DataExternalizer<List<Method>> {\n    override fun save(out: DataOutput, value: List<Method>) {\n        out.writeInt(value.size)\n\n        val methodExternalizer = MethodDataExternalizer()\n        value.forEach {\n            methodExternalizer.save(out, it)\n        }\n    }\n\n    override fun read(input: DataInput): List<Method> {\n        val size = input.readInt()\n        val methodExternalizer = MethodDataExternalizer()\n        val methods = mutableListOf<Method>()\n\n        repeat(size) {\n            methods.add(\n                methodExternalizer.read(input)\n            )\n        }\n\n        return methods\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/MethodDataExternalizer.kt",
    "content": "package com.pestphp.pest.features.customExpectations\n\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.jetbrains.php.lang.psi.stubs.indexes.StringSetDataExternalizer\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport com.pestphp.pest.features.customExpectations.generators.Parameter\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass MethodDataExternalizer : DataExternalizer<Method> {\n    override fun save(out: DataOutput, value: Method) {\n        EnumeratorStringDescriptor.INSTANCE.save(out, value.name)\n\n        var returnType = value.returnType.toString()\n        if (!value.returnType.isComplete) {\n            returnType = returnType.removeSuffix(\"|?\")\n        }\n\n        EnumeratorStringDescriptor.INSTANCE.save(\n            out,\n            returnType\n        )\n        StringSetDataExternalizer.INSTANCE.save(\n            out,\n            value.parameters\n                .map { it.toString() }\n                .toSet()\n        )\n    }\n\n    override fun read(input: DataInput): Method {\n        val name = EnumeratorStringDescriptor.INSTANCE.read(input)\n        val returnType = EnumeratorStringDescriptor.INSTANCE.read(input)\n        val parameterString = StringSetDataExternalizer.INSTANCE.read(input)\n\n        val parameters = parameterString.reversed().map {\n            Parameter(\n                name = Regex(\"name='(.*?)'\")\n                    .find(it)!!\n                    .groupValues[1],\n                returnType = PhpType.builder()\n                    .add(\n                        Regex(\"returnType='(.*?)'\")\n                            .find(it)!!\n                            .groupValues[1]\n                    ).build(),\n                defaultValue = Regex(\"defaultValue='(.*)'\")\n                    .find(it)!!\n                    .groupValues[1]\n                    .ifEmpty { null }\n            )\n        }\n\n        return Method(\n            name,\n            PhpType().add(returnType),\n            parameters\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/expectationUtil.kt",
    "content": "package com.pestphp.pest.features.customExpectations\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.jetbrains.php.PhpIndex\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.Function\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.ParameterList\nimport com.jetbrains.php.lang.psi.elements.PhpExpression\nimport com.jetbrains.php.lang.psi.elements.PhpNamespace\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.Variable\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport com.pestphp.pest.features.customExpectations.generators.Parameter\n\nval expectationType = PhpType().apply {\n    this.add(\"\\\\Pest\\\\Expectation\")\n}\n\nfun PhpType.isExpectation(project: Project): Boolean {\n    val filteredType = this.filterMixed()\n\n    if (filteredType.isEmpty) {\n        return false\n    }\n\n    return expectationType.isConvertibleFrom(\n        filteredType,\n        PhpIndex.getInstance(project)\n    )\n}\n\nfun Statement.isExpectation(): Boolean {\n    return (this.firstPsiChild as? MethodReference)?.isExpectation() == true\n}\n\nfun MethodReference.isExpectation(): Boolean {\n    return this.type.isExpectation(this.project)\n}\n\nfun PsiElement?.isThisVariableInExtend(): Boolean {\n    if ((this as? Variable)?.name != \"this\") return false\n\n    val closure = PsiTreeUtil.getParentOfType(this, Function::class.java)\n\n    if (closure == null || !closure.isClosure) return false\n\n    val parameterList = closure.parent?.parent as? ParameterList ?: return false\n\n    return parameterList.parent.isPestExtendReference()\n}\n\nfun PsiElement.isPestExtendReference(): Boolean {\n    if (this !is MethodReferenceImpl) {\n        return false\n    }\n\n    if (this.canonicalText != \"extend\") {\n        return false\n    }\n\n    val expectReference = this.firstChild\n\n    if (expectReference !is FunctionReferenceImpl) {\n        return false\n    }\n\n    if (expectReference.canonicalText != \"expect\") {\n        return false\n    }\n\n    return true\n}\n\nval MethodReference.extendName: String?\n    get() {\n        val name = this.getParameter(0) ?: return null\n\n        if (name !is StringLiteralExpression) {\n            return null\n        }\n\n        return name.contents\n    }\n\nval PsiFile.customExpects: List<MethodReferenceImpl>\n    get() {\n        if (this !is PhpFile) return emptyList()\n\n        val element = this.firstChild\n\n        return element.children.filterIsInstance<PhpNamespace>()\n            .mapNotNull { it.statements }\n            .getOrElse(\n                0\n            ) { element }\n            .children\n            .filterIsInstance<Statement>()\n            .mapNotNull { it.firstChild }\n            .filterIsInstance<MethodReferenceImpl>()\n            .filter { it.isPestExtendReference() }\n    }\n\nfun MethodReference.toMethod(): Method? {\n    val extendName = this.extendName ?: return null\n\n    // Custom expectations should always have two parameters.\n    if(this.parameters.count() != 2) {\n        return null\n    }\n\n    val closure = (this.parameters[1] as? PhpExpression)?.firstChild as? Function\n\n    if (closure === null) {\n        return null\n    }\n\n    return Method(\n        extendName,\n        closure.type,\n        closure.parameters.map { parameter ->\n            Parameter(\n                parameter.name,\n                parameter.type,\n                parameter.defaultValuePresentation\n            )\n        }\n    )\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/externalizers/ListDataExternalizer.kt",
    "content": "package com.pestphp.pest.features.customExpectations.externalizers\n\nimport com.intellij.util.io.DataExternalizer\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass ListDataExternalizer<K>(private val dataExternalizer: DataExternalizer<K>) : DataExternalizer<List<K>> {\n    override fun save(out: DataOutput, value: List<K>) {\n        out.writeInt(value.size)\n\n        value.forEach {\n            dataExternalizer.save(out, it)\n        }\n    }\n\n    override fun read(input: DataInput): List<K> {\n        val size = input.readInt()\n        val list = mutableListOf<K>()\n\n        repeat(size) {\n            list.add(\n                dataExternalizer.read(input)\n            )\n        }\n\n        return list\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/externalizers/MethodDataExternalizer.kt",
    "content": "package com.pestphp.pest.features.customExpectations.externalizers\n\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass MethodDataExternalizer : DataExternalizer<Method> {\n    companion object {\n        val INSTANCE = MethodDataExternalizer()\n    }\n\n    override fun save(out: DataOutput, value: Method) {\n        EnumeratorStringDescriptor.INSTANCE.save(out, value.name)\n        PhpTypeDataExternalizer.INSTANCE.save(out, value.returnType)\n        ListDataExternalizer(ParameterDataExternalizer.INSTANCE).save(\n            out,\n            value.parameters\n        )\n    }\n\n    override fun read(input: DataInput): Method {\n        val name = EnumeratorStringDescriptor.INSTANCE.read(input)\n        val returnType = PhpTypeDataExternalizer.INSTANCE.read(input)\n        val parameters = ListDataExternalizer(ParameterDataExternalizer.INSTANCE).read(input)\n\n        return Method(\n            name,\n            returnType,\n            parameters\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/externalizers/ParameterDataExternalizer.kt",
    "content": "package com.pestphp.pest.features.customExpectations.externalizers\n\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.intellij.util.io.NullableDataExternalizer\nimport com.pestphp.pest.features.customExpectations.generators.Parameter\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass ParameterDataExternalizer : DataExternalizer<Parameter> {\n    companion object {\n        val INSTANCE = ParameterDataExternalizer()\n    }\n\n    override fun save(out: DataOutput, value: Parameter) {\n        EnumeratorStringDescriptor.INSTANCE.save(out, value.name)\n        PhpTypeDataExternalizer.INSTANCE.save(out, value.returnType)\n        NullableDataExternalizer(EnumeratorStringDescriptor.INSTANCE).save(\n            out,\n            value.defaultValue\n        )\n    }\n\n    override fun read(input: DataInput): Parameter {\n        val name = EnumeratorStringDescriptor.INSTANCE.read(input)\n        val returnType = PhpTypeDataExternalizer.INSTANCE.read(input)\n        val defaultValue = NullableDataExternalizer(EnumeratorStringDescriptor.INSTANCE).read(input)\n\n        return Parameter(\n            name,\n            returnType,\n            defaultValue\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/externalizers/PhpTypeDataExternalizer.kt",
    "content": "package com.pestphp.pest.features.customExpectations.externalizers\n\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass PhpTypeDataExternalizer : DataExternalizer<PhpType> {\n    companion object {\n        val INSTANCE = PhpTypeDataExternalizer()\n    }\n\n    override fun save(out: DataOutput, value: PhpType) {\n        var endValue = value.toString()\n\n        if (!value.isComplete) {\n            endValue = endValue.removeSuffix(\"|?\")\n        }\n\n        EnumeratorStringDescriptor.INSTANCE.save(\n            out,\n            endValue\n        )\n    }\n\n    override fun read(input: DataInput): PhpType {\n        val type = EnumeratorStringDescriptor.INSTANCE.read(input)\n\n        return PhpType().add(type)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/generators/ExpectationGenerator.kt",
    "content": "package com.pestphp.pest.features.customExpectations.generators\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiFileFactory\nimport com.jetbrains.php.lang.PhpFileType\n\n/**\n * Generates A class for expectations.\n */\nclass ExpectationGenerator {\n    val docMethods: MutableList<Method> = mutableListOf()\n\n    fun generate(project: Project): String {\n        return docMethods\n            .joinToString(\"\\n\") { methodString(it, project) }\n            .let { //language=InjectablePHP\n                \"\"\"\n                |<?php\n                |\n                |namespace Pest;\n                |\n                |/**\n                |$it\n                | */\n                |final class Expectation {}\n                \"\"\".trimMargin()\n            }\n    }\n\n    private fun methodString(method: Method, project: Project): String {\n        val returnType = method.returnType.global(project).toStringResolved()\n        val parameters = method.parametersAsString(project).joinToString(separator = \", \")\n\n        return \" * @method $returnType ${method.name}($parameters)\"\n    }\n\n    fun generateToFile(project: Project): PsiFile {\n        return PsiFileFactory.getInstance(project).createFileFromText(\n            \"Expectation.\" + PhpFileType.INSTANCE.defaultExtension,\n            PhpFileType.INSTANCE,\n            generate(project)\n        )\n    }\n\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/generators/Method.kt",
    "content": "package com.pestphp.pest.features.customExpectations.generators\n\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\n\nclass Method(val name: String, val returnType: PhpType, val parameters: List<Parameter>) {\n    fun parametersAsString(project: Project): List<String> {\n        return parameters.map {\n            var parameterAsString = \"${it.returnType.global(project).toStringResolved()} $${it.name}\"\n\n            if (it.defaultValue !== null) {\n                parameterAsString += \" = ${it.defaultValue}\"\n            }\n\n            parameterAsString\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (javaClass != other?.javaClass) return false\n\n        other as Method\n\n        if (name != other.name) return false\n        if (returnType.toString() != other.returnType.toString()) return false\n\n\n        if (parameters != other.parameters) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = name.hashCode()\n        result = 31 * result + returnType.toString().hashCode()\n        result = 31 * result + parameters.hashCode()\n        return result\n    }\n\n    override fun toString(): String {\n        return \"Method(name='$name', returnType=$returnType, parameters=$parameters)\"\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/generators/Parameter.kt",
    "content": "package com.pestphp.pest.features.customExpectations.generators\n\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\n\nclass Parameter(val name: String, val returnType: PhpType, val defaultValue: String? = null) {\n    override fun toString(): String {\n        val defaultValue = defaultValue ?: \"\"\n\n        return \"Parameter(name='$name', returnType='$returnType', defaultValue='$defaultValue')\"\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (javaClass != other?.javaClass) return false\n\n        other as Parameter\n\n        if (name != other.name) return false\n        if (returnType.toString() != other.returnType.toString()) return false\n        if (defaultValue != other.defaultValue) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = name.hashCode()\n        result = 31 * result + returnType.toString().hashCode()\n        result = 31 * result + (defaultValue?.hashCode() ?: 0)\n        return result\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationReference.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.model.Symbol\nimport com.intellij.model.psi.PsiSymbolReference\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.pestphp.pest.features.customExpectations.KEY\nimport com.pestphp.pest.features.customExpectations.toMethod\n\nclass PestCustomExpectationReference(private val methodReference: MethodReference) : PsiSymbolReference {\n    override fun getElement(): MethodReference = methodReference\n\n    override fun getRangeInElement() = methodReference.rangeInElement\n\n    override fun resolveReference(): Collection<Symbol?> {\n        val pestCustomExtensions = mutableListOf<PestCustomExpectationSymbol>()\n        methodReference.name?.let {\n            val extensionName = methodReference.name!!\n            FileBasedIndex.getInstance()\n                .processValues(KEY, extensionName, null, { file, value ->\n                    methodReference.manager.findFile(file)?.let { psiFile ->\n                        val range = TextRange.from(value.first(), extensionName.length)\n                        val element = psiFile.findElementAt(range.startOffset)\n                        PhpPsiUtil.getParentOfClass(element, MethodReference::class.java)?.toMethod()?.let {\n                            pestCustomExtensions.add(PestCustomExpectationSymbol(extensionName, psiFile, range, it))\n                        }\n                    }\n                    true\n                }, GlobalSearchScope.allScope(methodReference.project))\n        }\n        return pestCustomExtensions\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationReferenceProvider.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.model.Symbol\nimport com.intellij.model.psi.PsiExternalReferenceHost\nimport com.intellij.model.psi.PsiSymbolReference\nimport com.intellij.model.psi.PsiSymbolReferenceHints\nimport com.intellij.model.psi.PsiSymbolReferenceProvider\nimport com.intellij.model.search.SearchRequest\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationReferenceProvider.Companion.PEST_EXPECTATION\n\nval PEST_EXPECTATION_TYPE: PhpType = PhpType.from(PEST_EXPECTATION)\n\nclass PestCustomExpectationReferenceProvider : PsiSymbolReferenceProvider {\n    companion object {\n        const val PEST_EXPECTATION: String = \"\\\\Pest\\\\Expectation\"\n    }\n\n    override fun getReferences(\n        element: PsiExternalReferenceHost,\n        hints: PsiSymbolReferenceHints\n    ): Collection<PsiSymbolReference> {\n        if (element is MethodReferenceImpl) {\n            val classReference = element.classReference\n            val methodName = element.name\n            if (methodName != null && classReference != null && \"extend\" != methodName &&\n                PhpType.intersectsGlobal(element.project, PEST_EXPECTATION_TYPE, classReference.type)\n            ) {\n                // workaround till `com.intellij.lang.javascript.navigation.JSGotoDeclarationHandler#getGotoDeclarationTargets` is not fixed\n                if (element.multiResolve(false).isEmpty()) {\n                    return arrayListOf(PestCustomExpectationReference(element))\n                }\n            }\n        }\n        return emptyList()\n    }\n\n    override fun getSearchRequests(project: Project, target: Symbol): Collection<SearchRequest> {\n        return (target as? PestCustomExpectationSymbol)?.let { listOf(SearchRequest.of(target.expectationName)) }\n            ?: emptyList()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationRenameUsageSearcher.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.find.usages.api.PsiUsage\nimport com.intellij.model.Pointer\nimport com.intellij.model.search.LeafOccurrenceMapper\nimport com.intellij.model.search.SearchContext\nimport com.intellij.model.search.SearchService\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.refactoring.rename.api.PsiModifiableRenameUsage\nimport com.intellij.refactoring.rename.api.RenameUsage\nimport com.intellij.refactoring.rename.api.RenameUsageSearchParameters\nimport com.intellij.refactoring.rename.api.RenameUsageSearcher\nimport com.intellij.util.AbstractQuery\nimport com.intellij.util.Processor\nimport com.intellij.util.Query\nimport com.jetbrains.php.lang.PhpLanguage\n\nprivate class PestCustomExpectationRenameUsageSearcher : RenameUsageSearcher {\n    override fun collectSearchRequests(parameters: RenameUsageSearchParameters): Collection<Query<out RenameUsage>> {\n        val targetSymbol = parameters.target as? PestCustomExpectationSymbol ?: return emptyList()\n        val symbolPointer: Pointer<PestCustomExpectationSymbol> = targetSymbol.createPointer()\n        val usages = SearchService.getInstance()\n            .searchWord(parameters.project, targetSymbol.expectationName)\n            .caseSensitive(true)\n            .inContexts(SearchContext.inCode())\n            .inFilesWithLanguage(PhpLanguage.INSTANCE)\n            .inScope(parameters.searchScope)\n            .buildQuery(LeafOccurrenceMapper.withPointer(symbolPointer, ::findReferencesToSymbol))\n        val selfUsage = PestCustomExtensionDeclarationUsageQuery(\n            PsiModifiableRenameUsage.defaultPsiModifiableRenameUsage(targetSymbol.declarationUsage())\n        )\n        return listOf(usages.mapping {\n            PsiModifiableRenameUsage.defaultPsiModifiableRenameUsage(\n                PsiUsage.textUsage(it.element.containingFile, it.element.nameNode!!.textRange)\n            )\n        }, selfUsage)\n    }\n}\n\ninternal class PestCustomExtensionDeclarationUsageQuery<T>(private val targetDeclaration: T) : AbstractQuery<T>() {\n    override fun processResults(p0: Processor<in T>): Boolean {\n        return runReadAction {\n            p0.process(targetDeclaration)\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationSymbol.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.find.usages.api.SearchTarget\nimport com.intellij.find.usages.api.UsageHandler\nimport com.intellij.model.Pointer\nimport com.intellij.model.Symbol\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.platform.backend.navigation.NavigationRequest\nimport com.intellij.platform.backend.navigation.NavigationTarget\nimport com.intellij.platform.backend.presentation.TargetPresentation\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.SearchScope\nimport com.intellij.refactoring.rename.api.RenameTarget\nimport com.pestphp.pest.features.customExpectations.generators.Method\n\nclass PestCustomExpectationSymbol(\n    @NlsSafe val expectationName: String,\n    val file: PsiFile,\n    val rangeInFile: TextRange,\n    val methodDescriptor: Method\n) : Symbol, NavigationTarget, SearchTarget, RenameTarget {\n\n    override fun createPointer() = Pointer.fileRangePointer(\n        file,\n        TextRange(rangeInFile.startOffset - 1, rangeInFile.endOffset + 1)\n    ) { restoredFile, restoredRange ->\n        // pointer doesn't survive when element is of zero range\n        PestCustomExpectationSymbol(\n            expectationName,\n            restoredFile,\n            TextRange(restoredRange.startOffset + 1, restoredRange.endOffset - 1),\n            methodDescriptor\n        )\n    }\n\n    override val maximalSearchScope: SearchScope\n        get() = GlobalSearchScope.projectScope(file.project)\n\n    override val targetName = expectationName\n\n    override fun presentation() = computePresentation()\n\n    override val usageHandler: UsageHandler\n        get() = UsageHandler.createEmptyUsageHandler(expectationName)\n\n    override fun equals(other: Any?) =\n        (other as? PestCustomExpectationSymbol)?.let { expectationName == it.expectationName } ?: false\n\n    override fun hashCode() = expectationName.hashCode()\n\n    override fun computePresentation() = TargetPresentation.builder(expectationName).presentation()\n\n    override fun navigationRequest() = NavigationRequest.sourceNavigationRequest(file, rangeInFile)\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationSymbolDeclaration.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.model.psi.PsiSymbolDeclaration\nimport com.intellij.openapi.util.TextRange\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.pestphp.pest.features.customExpectations.generators.Method\n\nclass PestCustomExpectationSymbolDeclaration(private val element: StringLiteralExpression, private val methodDescriptor: Method) : PsiSymbolDeclaration {\n    override fun getDeclaringElement() = element\n\n    override fun getRangeInDeclaringElement() = element.textRangeInParent\n\n    override fun getSymbol(): PestCustomExpectationSymbol {\n        val rangeInFile = TextRange.from(element.textRange.startOffset + 1, element.contents.length)\n        return PestCustomExpectationSymbol(element.contents, element.containingFile, rangeInFile, methodDescriptor)\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationSymbolDeclarationProvider.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.model.psi.PsiSymbolDeclaration\nimport com.intellij.model.psi.PsiSymbolDeclarationProvider\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.ParameterList\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.features.customExpectations.toMethod\n\nclass PestCustomExpectationSymbolDeclarationProvider : PsiSymbolDeclarationProvider {\n    override fun getDeclarations(element: PsiElement, offsetInElement: Int): Collection<PsiSymbolDeclaration> {\n        val possibleExtensionName = element as? StringLiteralExpression ?: return emptyList()\n        if (possibleExtensionName.parent !is ParameterList || possibleExtensionName.contents.isEmpty()) return emptyList()\n        val methodReference = possibleExtensionName.parent.parent as? MethodReferenceImpl ?: return emptyList()\n        val methodDescriptor = methodReference.toMethod() ?: return emptyList()\n        if (methodReference.name == \"extend\" &&\n            PhpType.intersectsGlobal(element.project, methodReference.classReference!!.type, PEST_EXPECTATION_TYPE)\n        ) {\n            return listOf(PestCustomExpectationSymbolDeclaration(element, methodDescriptor))\n        }\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/customExpectations/symbols/PestCustomExpectationUsageSearcher.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage com.pestphp.pest.features.customExpectations.symbols\n\nimport com.intellij.find.usages.api.PsiUsage\nimport com.intellij.find.usages.api.Usage\nimport com.intellij.find.usages.api.UsageSearchParameters\nimport com.intellij.find.usages.api.UsageSearcher\nimport com.intellij.model.Pointer\nimport com.intellij.model.search.LeafOccurrence\nimport com.intellij.model.search.LeafOccurrenceMapper\nimport com.intellij.model.search.SearchContext\nimport com.intellij.model.search.SearchService\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiFile\nimport com.intellij.util.Query\nimport com.jetbrains.php.lang.PhpLanguage\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.MethodReference\n\nclass PestCustomExpectationUsageSearcher : UsageSearcher {\n    override fun collectSearchRequests(parameters: UsageSearchParameters): Collection<Query<out Usage>> {\n        val targetSymbol = parameters.target as? PestCustomExpectationSymbol ?: return emptyList()\n        val symbolPointer: Pointer<PestCustomExpectationSymbol> = targetSymbol.createPointer()\n        val usages = SearchService.getInstance()\n            .searchWord(parameters.project, targetSymbol.expectationName).caseSensitive(true)\n            .inContexts(SearchContext.inCode()).inFilesWithLanguage(PhpLanguage.INSTANCE)\n            .inScope(parameters.searchScope)\n            .buildQuery(LeafOccurrenceMapper.withPointer(symbolPointer, ::findReferencesToSymbol))\n            .mapping { PsiUsage.textUsage(it.element.containingFile, it.element.nameNode!!.textRange) }\n        val selfUsage = PestCustomExtensionDeclarationUsageQuery(targetSymbol.declarationUsage())\n        return listOf(usages, selfUsage)\n    }\n}\n\nfun PestCustomExpectationSymbol.declarationUsage() = PestCustomDeclarationUsage(file, rangeInFile)\n\nclass PestCustomDeclarationUsage(\n    override val file: PsiFile, override val range: TextRange\n) : PsiUsage {\n    override val declaration: Boolean\n        get() = true\n\n    override fun createPointer() = PsiUsage.textUsage(file, range).createPointer()\n}\n\nfun findReferencesToSymbol(\n    symbol: PestCustomExpectationSymbol,\n    leafOccurrence: LeafOccurrence\n): Collection<PestCustomExpectationReference> {\n    val methodReference = PhpPsiUtil.getParentOfClass(leafOccurrence.start, MethodReference::class.java)\n    if (methodReference?.nameNode == null) return emptyList()\n    val possibleReference = PestCustomExpectationReference(methodReference)\n    return if (possibleReference.resolvesTo(symbol)) listOf(possibleReference) else emptyList()\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/DatasetIndex.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.indexing.DataIndexer\nimport com.intellij.util.indexing.DefaultFileTypeSpecificInputFilter\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.intellij.util.indexing.FileBasedIndexExtension\nimport com.intellij.util.indexing.FileContent\nimport com.intellij.util.indexing.ID\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.intellij.util.io.KeyDescriptor\nimport com.jetbrains.php.lang.PhpFileType\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.pestphp.pest.features.customExpectations.externalizers.ListDataExternalizer\nimport com.pestphp.pest.realPath\n\nval key = ID.create<String, List<String>>(\"php.pest.datasets\")\n\n/**\n * Indexes all pest datasets with the following key value store\n *  `path/datasets/file => ['my-dataset', 'my-other-dataset']\n */\nclass DatasetIndex : FileBasedIndexExtension<String, List<String>>() {\n    override fun getName(): ID<String, List<String>> {\n        return key\n    }\n\n    override fun getVersion(): Int {\n        return 3\n    }\n\n    override fun getIndexer(): DataIndexer<String, List<String>, FileContent> {\n        return DataIndexer { inputData ->\n            val file = inputData.psiFile\n\n            if (file !is PhpFile) {\n                return@DataIndexer mapOf()\n            }\n\n            val datasets = file\n                .getDatasets()\n                .mapNotNull { it.getPestDatasetName() }\n\n            if (datasets.isEmpty()) {\n                return@DataIndexer mapOf()\n            }\n\n            mapOf(\n                file.realPath to datasets\n            )\n        }\n    }\n\n    override fun getKeyDescriptor(): KeyDescriptor<String> {\n        return EnumeratorStringDescriptor.INSTANCE\n    }\n\n    override fun getValueExternalizer(): DataExternalizer<List<String>> {\n        return ListDataExternalizer(EnumeratorStringDescriptor.INSTANCE)\n    }\n\n    override fun getInputFilter(): FileBasedIndex.InputFilter {\n      return object : DefaultFileTypeSpecificInputFilter(PhpFileType.INSTANCE) {\n        override fun acceptInput(file: VirtualFile): Boolean {\n          return super.acceptInput(file) && file.parent.path.endsWith(\"/Datasets\")\n        }\n      }\n    }\n\n    override fun dependsOnFileContent(): Boolean {\n        return true\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/DatasetReference.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiElementResolveResult\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.PsiPolyVariantReference\nimport com.intellij.psi.PsiReferenceBase\nimport com.intellij.psi.ResolveResult\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\n\n\n/**\n * Used to make a reference between a string and a dataset function call\n */\nclass DatasetReference(\n    element: StringLiteralExpression\n) : PsiReferenceBase<StringLiteralExpression>(element), PsiPolyVariantReference {\n    override fun resolve(): PsiElement? {\n        return multiResolve(false).firstOrNull()?.element\n    }\n\n    override fun getVariants(): Array<Any> {\n        return getAllDatasets()\n            .mapNotNull { it.getPestDatasetName() }\n            .toTypedArray()\n    }\n\n    override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {\n        val fileBasedIndex = FileBasedIndex.getInstance()\n        val datasetName = element.contents\n        val foundDatasets = mutableListOf<FunctionReferenceImpl>()\n\n        fileBasedIndex.getAllKeys(\n          key,\n          element.project\n        ).forEach { key ->\n            fileBasedIndex.processValues(\n              com.pestphp.pest.features.datasets.key,\n              key,\n              null,\n              { file, datasets ->\n                    if (datasetName !in datasets) {\n                        return@processValues true\n                    }\n\n                    // Add all shared datasets which matches\n                    PsiManager.getInstance(element.project).findFile(file)!!\n                        .getDatasets()\n                        .filter { it.getPestDatasetName() == datasetName }\n                        .forEach { foundDatasets.add(it) }\n\n                    true\n                },\n              GlobalSearchScope.projectScope(element.project)\n            )\n        }\n\n        // Add all local datasets which matches\n        element.containingFile\n            .getDatasets()\n            .filter { it.getPestDatasetName() == datasetName }\n            .forEach { foundDatasets.add(it) }\n\n        return foundDatasets.map { PsiElementResolveResult(it) }\n            .toTypedArray()\n    }\n\n    private fun getAllDatasets(): MutableList<FunctionReferenceImpl> {\n        val fileBasedIndex = FileBasedIndex.getInstance()\n        val foundDatasets = mutableListOf<FunctionReferenceImpl>()\n\n        fileBasedIndex.getAllKeys(\n          key,\n          element.project\n        ).forEach { key ->\n            fileBasedIndex.processValues(\n              com.pestphp.pest.features.datasets.key,\n              key,\n              null,\n              { file, _ ->\n                    // Add all datasets\n                    PsiManager.getInstance(element.project).findFile(file)!!\n                        .getDatasets()\n                        .forEach { foundDatasets.add(it) }\n\n                    true\n                },\n              GlobalSearchScope.projectScope(element.project)\n            )\n        }\n\n        // Add all local datasets which matches\n        element.containingFile\n            .getDatasets()\n            .forEach { foundDatasets.add(it) }\n\n        return foundDatasets\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/DatasetReferenceContributor.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.patterns.PlatformPatterns\nimport com.intellij.psi.PsiReferenceContributor\nimport com.intellij.psi.PsiReferenceRegistrar\nimport com.jetbrains.php.lang.psi.elements.ParameterList\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\n\n/**\n * Used to register all dataset reference provider\n */\nclass DatasetReferenceContributor : PsiReferenceContributor() {\n    override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {\n        registrar.registerReferenceProvider(\n            PlatformPatterns.psiElement(StringLiteralExpression::class.java)\n                .withParents(\n                    ParameterList::class.java,\n                    MethodReferenceImpl::class.java,\n                ),\n            DatasetReferenceProvider()\n        )\n    }\n}\n\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/DatasetReferenceProvider.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiReference\nimport com.intellij.psi.PsiReferenceProvider\nimport com.intellij.util.ProcessingContext\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\n\n/**\n * Adds goto and reference support to string literals referring datasets\n */\nclass DatasetReferenceProvider : PsiReferenceProvider() {\n    override fun getReferencesByElement(\n        element: PsiElement,\n        context: ProcessingContext\n    ): Array<PsiReference> {\n        if (element !is StringLiteralExpression) {\n            return PsiReference.EMPTY_ARRAY\n        }\n\n        val methodReference = element.parent?.parent as? MethodReference ?: return PsiReference.EMPTY_ARRAY\n\n        if (!methodReference.isDatasetCall()) return PsiReference.EMPTY_ARRAY\n\n        return arrayOf(\n            DatasetReference(element)\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/DatasetUtil.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.collectFromDescribeBlocks\nimport com.pestphp.pest.getRootPhpPsiElements\nimport com.pestphp.pest.isPestTestReference\nimport com.pestphp.pest.realPath\n\nfun PsiFile.isIndexedPestDatasetFile(): Boolean {\n    return FileBasedIndex.getInstance().getValues(\n      key,\n      this.realPath,\n      ProjectScope.getProjectScope(this.project)\n    ).isNotEmpty()\n}\n\nfun PsiElement?.isPestDataset(): Boolean {\n    return when (this) {\n        is FunctionReferenceImpl -> this.isPestDatasetFunction()\n        else -> false\n    }\n}\n\nfun FunctionReferenceImpl.isPestDatasetFunction(): Boolean {\n    return this.canonicalText in setOf(\"dataset\")\n}\n\nfun FunctionReferenceImpl.getPestDatasetName(): String? {\n    return (getParameter(0) as? StringLiteralExpression)?.contents\n}\n\nfun MethodReference.isDatasetCall() : Boolean {\n    if (name != \"with\") return false\n\n    return isPestTestReference()\n}\n\nfun PsiFile.getDatasets(): List<FunctionReferenceImpl> {\n    return collectFromDescribeBlocks(this.getRootPhpPsiElements()) { element ->\n        (element as? FunctionReferenceImpl)?.takeIf { it.isPestDataset() }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/InvalidDatasetNameCaseInspection.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.modcommand.ActionContext\nimport com.intellij.modcommand.ModPsiUpdater\nimport com.intellij.modcommand.PsiUpdateModCommandAction\nimport com.intellij.psi.PsiElementVisitor\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.PhpPsiElementFactory\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.getInitialFunctionReference\nimport com.pestphp.pest.getRootPhpPsiElements\nimport com.pestphp.pest.goto.getDatasetUsages\nimport com.pestphp.pest.inspections.convertTestNameToSentenceCase\nimport com.pestphp.pest.inspections.isInvalidNameCase\n\nclass InvalidDatasetNameCaseInspection : PhpInspection() {\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return object : PhpElementVisitor() {\n            override fun visitPhpFile(file: PhpFile) {\n                val localDatasets = file.getRootPhpPsiElements()\n                    .filter { it.isPestDataset() }\n                    .filterIsInstance<FunctionReferenceImpl>()\n\n                localDatasets.groupBy { it.getPestDatasetName() }\n                    .filterKeys { datasetName ->\n                        datasetName != null && isInvalidNameCase(datasetName)\n                    }\n                    .forEach {\n                        declareProblemType(holder, it.value)\n                    }\n            }\n        }\n    }\n\n    private fun declareProblemType(holder: ProblemsHolder, datasets: List<FunctionReference>) {\n        datasets.mapNotNull { it.getInitialFunctionReference()?.getParameter(0) }\n            .filterIsInstance<StringLiteralExpression>()\n            .forEach {\n                holder.problem(it, PestBundle.message(\"INSPECTION_INVALID_DATASET_NAME_CASE\"))\n                    .fix(ChangeDatasetNameCasingQuickFix(it))\n                    .register()\n            }\n    }\n\n    private class ChangeDatasetNameCasingQuickFix(\n        datasetDeclarationName: StringLiteralExpression\n    ) : PsiUpdateModCommandAction<StringLiteralExpression>(datasetDeclarationName) {\n        override fun getFamilyName(): String {\n            return PestBundle.message(\"QUICK_FIX_CHANGE_DATASET_NAME_CASING\")\n        }\n\n        override fun invoke(context: ActionContext, datasetNamePsiElement: StringLiteralExpression, updater: ModPsiUpdater) {\n            val sentenceCaseDatasetName = convertTestNameToSentenceCase(datasetNamePsiElement.contents)\n            val newNameParameter = PhpPsiElementFactory.createStringLiteralExpression(\n                datasetNamePsiElement.project,\n                sentenceCaseDatasetName,\n                true\n            )\n\n            val datasetUsages = getDatasetUsages(datasetNamePsiElement)?.map { updater.getWritable(it) }\n            datasetUsages?.forEach {\n                val testWithDataset = it as? MethodReference ?: return@forEach\n                val nameParameter = testWithDataset.getParameter(0) as? StringLiteralExpression ?: return@forEach\n\n                nameParameter.replace(newNameParameter.copy())\n            }\n\n            datasetNamePsiElement.replace(newNameParameter.copy())\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/datasets/InvalidDatasetReferenceInspection.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.codeInspection.LocalQuickFix\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.psi.PsiElementVisitor\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.getPestTests\n\nclass InvalidDatasetReferenceInspection : PhpInspection() {\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return object : PhpElementVisitor() {\n            override fun visitPhpFile(file: PhpFile) {\n                val localDatasets = file.getDatasets()\n                    .mapNotNull { it.getPestDatasetName() }\n                // Get all shared datasets\n                val fileBasedIndex = FileBasedIndex.getInstance()\n                val sharedDatasets = fileBasedIndex.getAllKeys(key, file.project)\n                    .map {\n                        fileBasedIndex.getValues(\n                          key,\n                          it,\n                          GlobalSearchScope.projectScope(file.project)\n                        )\n                    }\n                    .flatten()\n                    .flatten()\n\n                file.getPestTests()\n                    // Has to be a method reference, as else there is no dataset\n                    .asSequence()\n                    .filterIsInstance<MethodReferenceImpl>()\n                    .filter { it.name == \"with\" }\n                    .mapNotNull { it.parameters.getOrNull(0) }\n                    .filterIsInstance<StringLiteralExpression>()\n                    .filter { it.contents !in localDatasets && it.contents !in sharedDatasets }\n                    .toList()\n                    .forEach {\n                        declareProblemType(\n                            holder,\n                            it\n                        )\n                    }\n            }\n        }\n    }\n\n    @Suppress(\"SpreadOperator\")\n    private fun declareProblemType(holder: ProblemsHolder, datasetName: StringLiteralExpression) {\n        holder.registerProblem(\n            datasetName,\n            PestBundle.message(\"INSPECTION_INVALID_DATASET_REFERENCE\"),\n            *LocalQuickFix.EMPTY_ARRAY\n        )\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/parallel/PestParallelProgramRunner.kt",
    "content": "package com.pestphp.pest.features.parallel\n\nimport com.intellij.execution.ExecutionException\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.configurations.RunnerSettings\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.process.ProcessListener\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.runners.GenericProgramRunner\nimport com.intellij.execution.runners.RunContentBuilder\nimport com.intellij.execution.testframework.sm.runner.SMTestProxy.SMRootTestProxy\nimport com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.notification.NotificationType\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.util.NlsSafe\nimport com.jetbrains.php.PhpBundle\nimport com.jetbrains.php.config.commandLine.PhpCommandSettings\nimport com.jetbrains.php.config.commandLine.PhpCommandSettingsBuilder\nimport com.jetbrains.php.phpunit.PhpUnitUtil\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestFrameworkType\nimport com.pestphp.pest.configuration.PestRerunProfile\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.configuration.PestVersionDetector\nimport com.pestphp.pest.statistics.PestUsagesCollector\n\ninternal val PEST_PARALLEL_ARGUMENTS = mutableListOf(\"--parallel\", \"--log-teamcity\", \"php://stdout\")\n\nclass PestParallelProgramRunner : GenericProgramRunner<RunnerSettings>() {\n    companion object {\n        const val RUNNER_ID: String = \"PestParallelRunner\"\n    }\n\n    override fun canRun(executorId: String, profile: RunProfile): Boolean =\n        executorId == PestParallelTestExecutor.EXECUTOR_ID && profile is PestRunConfiguration\n\n    override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {\n        PestUsagesCollector.logParallelTestExecution(environment.project)\n\n        val executionResult = if (environment.runProfile is PestRerunProfile) {\n            state.execute(environment.executor, this)\n        } else {\n            val runConfiguration = environment.runProfile as? PestRunConfiguration\n                ?: throw ExecutionException(PestBundle.message(\"PEST_PARALLEL_IS_NOT_SUPPORTED_FOR_SELECTED_RUN_PROFILE\"))\n            val command = createPestParallelCommand(runConfiguration)\n            runConfiguration.checkAndGetState(environment, command)?.execute(environment.executor, this)\n        }\n        if (executionResult == null) throw ExecutionException(PhpBundle.message(\"execution.result.is.null\"))\n        val contentDescriptor = RunContentBuilder(executionResult, environment).showRunContent(environment.contentToReuse)\n        postprocessExecutionResult(contentDescriptor, environment, PestBundle.message(\"PARALLEL_TESTING_IS_SUPPORTED_FROM_VERSION_2\"))\n        return contentDescriptor\n    }\n\n    override fun getRunnerId(): String = RUNNER_ID\n\n    fun getArguments(): MutableList<String> = PEST_PARALLEL_ARGUMENTS\n}\n\ninternal fun createPestParallelCommand(runConfiguration: PestRunConfiguration): PhpCommandSettings {\n    FileDocumentManager.getInstance().saveAllDocuments()\n    val interpreter = runConfiguration.interpreter ?: throw ExecutionException(PhpCommandSettingsBuilder.getInterpreterNotFoundError())\n    return runConfiguration.createCommand(\n        interpreter,\n        mutableMapOf(),\n        if (executeInParallel(runConfiguration)) mutableListOf() else PEST_PARALLEL_ARGUMENTS,\n        false\n    )\n}\n\nfun postprocessExecutionResult(\n    contentDescriptor: RunContentDescriptor,\n    environment: ExecutionEnvironment,\n    @NlsSafe versionRequirement: String,\n) {\n    val processHandler = contentDescriptor.processHandler\n    processHandler?.addProcessListener(object : ProcessListener {\n        override fun processTerminated(event: ProcessEvent) {\n            val executionConsole = contentDescriptor.executionConsole as? SMTRunnerConsoleView ?: return\n            val rootProxy = executionConsole.resultsViewer.root as? SMRootTestProxy ?: return\n            if (rootProxy.isEmptySuite && !rootProxy.isTestsReporterAttached) {\n                handleEmptySuite(environment, versionRequirement)\n            }\n        }\n    })\n}\n\nprivate fun handleEmptySuite(\n    environment: ExecutionEnvironment,\n    @NlsSafe versionRequirement: String,\n) {\n    val profile = environment.runProfile as PestRunConfiguration\n    val project = profile.project\n    val interpreter = profile.interpreter ?: return\n    val config = PhpTestFrameworkSettingsManager.getInstance(project).getOrCreateByInterpreter(\n        PestFrameworkType.instance, interpreter, profile.getBaseFile(null, interpreter), true\n    ) ?: return\n\n    val version = if (!interpreter.isRemote) {\n        PestVersionDetector.instance.getVersion(project, interpreter, config.executablePath)\n    } else {\n        null\n    }\n\n    createAndShowNotification(environment, versionRequirement, version)\n}\n\nprivate fun createAndShowNotification(\n    environment: ExecutionEnvironment,\n    @NlsSafe versionRequirement: String,\n    version: String?,\n) {\n    PhpUnitUtil.getNotificationGroup().createNotification(\n        versionRequirement,\n        NotificationType.ERROR\n    ).apply {\n        version?.let {\n            setTitle(PestBundle.message(\"CURRENT_PEST_VERSION_IS\", it))\n        }\n        setSuggestionType(true)\n        notify(environment.project)\n    }\n}\n\ninternal fun executeInParallel(runConfiguration: RunConfiguration): Boolean {\n    return runConfiguration is PestRunConfiguration && runConfiguration.pestSettings.pestRunnerSettings.parallelTestingEnabled\n}\n\nfun addParallelArguments(runConfiguration: PestRunConfiguration, command: PhpCommandSettings) {\n    if (executeInParallel(runConfiguration)) {\n        PestUsagesCollector.logParallelTestExecution(runConfiguration.project)\n        command.addArguments(PEST_PARALLEL_ARGUMENTS)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/parallel/PestParallelSMTEventsAdapter.kt",
    "content": "package com.pestphp.pest.features.parallel\n\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerEventsAdapter\nimport com.intellij.execution.testframework.sm.runner.SMTestProxy\n\nclass PestParallelSMTEventsAdapter : SMTRunnerEventsAdapter() {\n    override fun onSuiteStarted(suite: SMTestProxy) {\n        suite.setPresentableName(convertSuiteNameToClassName(suite.name))\n    }\n\n    override fun onTestStarted(test: SMTestProxy) {\n        test.setPresentableName(convertRuntimeTestNameToRealTestName(test.name))\n    }\n}\n\nprivate const val PLACEHOLDER = \" \"\n\ninternal fun convertRuntimeTestNameToRealTestName(runtimeTestName: String): String =\n    runtimeTestName\n        .removePrefix(\"__pest_evaluable_\")\n        .replace(\"__→_\", \"  → \")\n        .replace(\"__\", PLACEHOLDER)\n        .replace(\"_\", \" \")\n        .replace(PLACEHOLDER, \"_\")\n\nprivate fun convertSuiteNameToClassName(suiteName: String): String =\n    suiteName.removePrefix(\"P\\\\\")"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/parallel/PestParallelTestExecutor.kt",
    "content": "package com.pestphp.pest.features.parallel\n\nimport com.intellij.execution.Executor\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.util.IconLoader.getDisabledIcon\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.openapi.util.text.TextWithMnemonic\nimport com.intellij.openapi.wm.ToolWindowId\nimport com.jetbrains.php.PhpIcons\nimport com.pestphp.pest.PestBundle\nimport org.jetbrains.annotations.Nls\nimport org.jetbrains.annotations.NonNls\nimport javax.swing.Icon\n\nclass PestParallelTestExecutor : Executor() {\n    companion object {\n        const val EXECUTOR_ID: @NonNls String = \"PestParallelTestExecutor\"\n        const val CONTEXT_ACTION_ID: @NonNls String = \"PestParallelRun\"\n    }\n\n    override fun getToolWindowId(): String = ToolWindowId.RUN\n\n    override fun getToolWindowIcon(): Icon = AllIcons.Toolwindows.ToolWindowRun\n\n    override fun getIcon(): Icon = PhpIcons.RUN_PARA_TEST\n\n    override fun getRerunIcon(): Icon = AllIcons.Actions.Rerun\n\n    override fun getDisabledIcon(): Icon = getDisabledIcon(icon)\n\n    override fun getDescription(): String = PestBundle.message(\"ACTION_RUN_SELECTED_CONFIGURATION_WITH_PARALLEL_DESCRIPTION\")\n\n    override fun getActionName(): String = PestBundle.message(\"ACTION_PEST_PARALLEL_TEXT\")\n\n    override fun getId(): String = EXECUTOR_ID\n\n    override fun getStartActionText(): @Nls(capitalization = Nls.Capitalization.Title) String = PestBundle.message(\"RUN_PEST_WITH_PARALLEL\")\n\n    override fun getStartActionText(configurationName: String): String {\n        val configName = if (StringUtil.isEmpty(configurationName)) \"\" else \" '${shortenNameIfNeeded(configurationName)}'\"\n        return TextWithMnemonic.parse(PestBundle.message(\"RUN_S_WITH_PARALLEL\")).replaceFirst(\"%s\", configName).toString()\n    }\n\n    override fun getContextActionId(): String = CONTEXT_ACTION_ID\n\n    override fun getHelpId(): String? = null\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/snapshotTesting/SnapshotLineMarkerProvider.kt",
    "content": "package com.pestphp.pest.features.snapshotTesting\n\nimport com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo\nimport com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider\nimport com.intellij.codeInsight.navigation.NavigationGutterIconBuilder\nimport com.intellij.icons.AllIcons\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.lexer.PhpTokenTypes\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.PhpUse\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.PestBundle\n\nprivate class SnapshotLineMarkerProvider : RelatedItemLineMarkerProvider() {\n    override fun collectNavigationMarkers(\n        element: PsiElement,\n        result: MutableCollection<in RelatedItemLineMarkerInfo<*>>,\n    ) {\n        if (!PhpPsiUtil.isOfType(element, PhpTokenTypes.IDENTIFIER)) {\n            return\n        }\n\n        val functionReference = element.parent as? FunctionReferenceImpl ?: return\n\n        if (!functionReference.isSnapshotAssertionCall) {\n            return\n        }\n\n        if (functionReference.parent is PhpUse) {\n            return\n        }\n\n        val snapshotFiles = functionReference.snapshotFiles\n\n        val builder = NavigationGutterIconBuilder.create(AllIcons.Nodes.DataSchema)\n            .setTargets(snapshotFiles)\n            .setTooltipText(PestBundle.message(\"TOOLTIP_NAVIGATE_TO_SNAPSHOT_FILES\"))\n        result.add(builder.createLineMarkerInfo(element))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/features/snapshotTesting/SnapshotUtil.kt",
    "content": "package com.pestphp.pest.features.snapshotTesting\n\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.util.parentOfType\nimport com.jetbrains.php.codeInsight.controlFlow.PhpControlFlowUtil\nimport com.jetbrains.php.codeInsight.controlFlow.PhpInstructionProcessor\nimport com.jetbrains.php.codeInsight.controlFlow.instructions.PhpCallInstruction\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionImpl\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isPestTestReference\n\nval snapshotAssertionNames = listOf(\n    \"assertMatchesSnapshot\",\n    \"assertMatchesFileHashSnapshot\",\n    \"assertMatchesFileSnapshot\",\n    \"assertMatchesHtmlSnapshot\",\n    \"assertMatchesJsonSnapshot\",\n    \"assertMatchesObjectSnapshot\",\n    \"assertMatchesTextSnapshot\",\n    \"assertMatchesXmlSnapshot\",\n    \"assertMatchesYamlSnapshot\",\n)\n\nval FunctionReferenceImpl.isSnapshotAssertionCall: Boolean\n    get() {\n        return snapshotAssertionNames.contains(this.name)\n    }\n\nval FunctionReferenceImpl.snapshotFiles: List<PsiFile>\n    get() {\n        val pestBody = this.parentOfType<FunctionImpl>() ?: return emptyList()\n\n        // Make sure we are inside a pest test\n        val pestTestReference = pestBody.parent?.parent?.parent ?: return emptyList()\n\n        if (!pestTestReference.isPestTestReference()) {\n            return emptyList()\n        }\n\n        val snapshotDirectory = this.project.guessProjectDir()\n            ?.findFileByRelativePath(\"tests/__snapshots__\") ?: return emptyList()\n        val testFileName = this.containingFile.name.removeSuffix(\".php\")\n        val testName = pestTestReference.getPestTestName() ?: return emptyList()\n\n        val snapshotFiles = mutableListOf<PsiFile>()\n        val snapshotCalls = pestBody.getSnapshotCallNumber(this)\n\n\n        ProjectFileIndex.getInstance(this.project)\n            .iterateContentUnderDirectory(\n                snapshotDirectory\n            ) {\n                val psiFile = PsiManager.getInstance(this.project)\n                    .findFile(it) ?: return@iterateContentUnderDirectory true\n\n                if (!psiFile.isSnapshotFile(\n                        testName,\n                        testFileName,\n                        snapshotCalls\n                    )\n                ) {\n                    return@iterateContentUnderDirectory true\n                }\n\n                snapshotFiles.add(psiFile)\n                true\n            }\n\n        return snapshotFiles\n    }\n\nprivate fun PsiFile.isSnapshotFile(testName: String, testFileName: String, snapshotCall: Int): Boolean {\n    val snapshotFileName = this.virtualFile.nameWithoutExtension\n    this.virtualFile.extension ?: return false\n\n    val testNameUnderscore = testName.replace(' ', '_')\n\n    if (!snapshotFileName.startsWith(\"${testFileName}__$testNameUnderscore\")) {\n        return false\n    }\n\n    if (!snapshotFileName.endsWith(\"__$snapshotCall\")) {\n        return false\n    }\n\n    return true\n}\n\nprivate fun FunctionImpl.getSnapshotCallNumber(snapshotFunctionReference: FunctionReferenceImpl): Int {\n    var snapshotCalls = 0\n    val processor: PhpInstructionProcessor = object : PhpInstructionProcessor() {\n        override fun processPhpCallInstruction(instruction: PhpCallInstruction): Boolean {\n            val functionReference = instruction.functionReference\n\n            if (functionReference !is FunctionReferenceImpl) {\n                return super.processPhpCallInstruction(instruction)\n            }\n\n            if (!functionReference.isSnapshotAssertionCall) {\n                return super.processPhpCallInstruction(instruction)\n            }\n            snapshotCalls++\n\n            if (PsiManager.getInstance(functionReference.project).areElementsEquivalent(\n                    functionReference,\n                    snapshotFunctionReference\n                )\n            ) {\n                return false\n            }\n\n            return super.processPhpCallInstruction(instruction)\n        }\n    }\n\n    val flow = controlFlow\n    PhpControlFlowUtil.processSuccessors(flow.entryPoint, false, processor)\n    return snapshotCalls\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/goto/PestDatasetUsagesGotoHandler.kt",
    "content": "package com.pestphp.pest.goto\n\nimport com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.PsiSearchHelper\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.util.Processor\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.features.datasets.isDatasetCall\nimport com.pestphp.pest.features.datasets.isPestDatasetFunction\n\nfun getDatasetUsages(literal: StringLiteralExpression): Array<PsiElement>? {\n    val function = literal.parent?.parent as? FunctionReferenceImpl ?: return null\n\n    if (!function.isPestDatasetFunction()) return null\n\n    val searchHelper = PsiSearchHelper.getInstance(literal.project)\n    val result = mutableListOf<PsiElement>()\n    val datasetName = literal.contents\n\n    val processor = Processor { psiFile: PsiFile ->\n        result.addAll(\n            PsiTreeUtil.findChildrenOfType(psiFile, StringLiteralExpression::class.java).filter {\n                it.contents == datasetName\n            }.mapNotNull {\n                it.parent?.parent as? MethodReference\n            }.filter {\n                it.isDatasetCall()\n            }\n        )\n\n        true\n    }\n\n    searchHelper.processAllFilesWithWordInLiterals(\n        datasetName,\n        GlobalSearchScope.allScope(literal.project),\n        processor,\n    )\n\n    return result.toTypedArray()\n}\n\nclass PestDatasetUsagesGotoHandler : GotoDeclarationHandler {\n    override fun getGotoDeclarationTargets(\n        sourceElement: PsiElement?,\n        offset: Int,\n        editor: Editor?\n    ): Array<PsiElement>? {\n        val literal = sourceElement?.parent as? StringLiteralExpression ?: return null\n        return getDatasetUsages(literal)\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/goto/PestGotoTargetPresentationProvider.kt",
    "content": "package com.pestphp.pest.goto\n\nimport com.intellij.codeInsight.navigation.GotoTargetPresentationProvider\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.platform.backend.presentation.TargetPresentation\nimport com.intellij.psi.PsiElement\nimport com.pestphp.pest.PestIcons\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isPestTestReference\n\nclass PestGotoTargetPresentationProvider: GotoTargetPresentationProvider {\n    override fun getTargetPresentation(element: PsiElement, differentNames: Boolean): TargetPresentation? {\n        if (element.isPestTestReference()) {\n            @NlsSafe val pestTestName = element.getPestTestName()\n            return TargetPresentation.builder(pestTestName ?: element.containingFile.name)\n                .containerText(element.containingFile?.presentation?.locationString)\n                .icon(PestIcons.Logo)\n                .presentation()\n        }\n        return null\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/goto/PestTestFinder.kt",
    "content": "package com.pestphp.pest.goto\n\nimport com.intellij.openapi.util.Pair\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.testIntegration.TestFinder\nimport com.intellij.testIntegration.TestFinderHelper\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.PhpIndex\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.Method\nimport com.jetbrains.php.lang.psi.elements.PhpClass\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\nimport com.pestphp.pest.indexers.key\nimport com.pestphp.pest.inspections.convertTestNameToSentenceCase\nimport com.pestphp.pest.isPestTestFile\n\nclass PestTestFinder : TestFinder {\n    /**\n     * @return methods if the given element is a psi child of Pest function call,\n     *         classes otherwise\n     */\n    override fun findClassesForTest(element: PsiElement): Collection<PsiElement> {\n        val classes = PhpIndex.getInstance(element.project)\n            .getClassesByNameInScope(\n                element.containingFile.name.removeSuffix(\"Test.php\"),\n                GlobalSearchScope.projectScope(element.project)\n            )\n\n        val testName = PsiTreeUtil.getNonStrictParentOfType(element, FunctionReference::class.java)\n            ?.getPestTestName()\n            ?.split(\" \")\n            ?.joinToString(\"\")\n            ?: return classes\n        val methodsAndProximityScores = classes.flatMap { phpClass -> phpClass.ownMethods.toList() }\n            .filter { method -> testName.contains(method.name, ignoreCase = true) }\n            .map { method -> Pair(method, TestFinderHelper.calcTestNameProximity(method.name, testName)) }\n        return if (!methodsAndProximityScores.isEmpty())\n            TestFinderHelper.getSortedElements(methodsAndProximityScores, true)\n        else\n            classes\n    }\n\n    override fun findSourceElement(from: PsiElement): PsiElement? {\n        return from.containingFile\n    }\n\n    override fun isTest(element: PsiElement): Boolean {\n        if (element is PhpClass) return false\n        return element.containingFile.isPestTestFile()\n    }\n\n    override fun findTestsForClass(element: PsiElement): Collection<PsiElement> {\n        val parent = PsiTreeUtil.getNonStrictParentOfType(element, PhpClass::class.java, Method::class.java) ?: return arrayListOf()\n\n        return when (parent) {\n            is PhpClass -> findTestFilesForClass(parent)\n            is Method -> findTestsForMethod(parent)\n            else -> arrayListOf()\n        }\n    }\n\n    private fun findTestsForMethod(method: Method): List<FunctionReference> {\n        val phpClass = method.containingClass ?: return emptyList()\n        val sentenceCaseMethodName = convertTestNameToSentenceCase(method.name)\n\n        val testsAndProximityScores = findTestFilesForClass(phpClass)\n            .flatMap { psiFile ->\n                psiFile.getPestTests().mapNotNull { test ->\n                    val testName = test.getPestTestName() ?: return@mapNotNull null\n                    val sentenceCaseTestName = if (testName.contains(' ')) testName else convertTestNameToSentenceCase(testName)\n                    if (sentenceCaseTestName.contains(sentenceCaseMethodName, ignoreCase = true)) {\n                        Pair(test, TestFinderHelper.calcTestNameProximity(sentenceCaseMethodName, sentenceCaseTestName))\n                    } else {\n                        null\n                    }\n                }\n            }\n        return testsAndProximityScores.sortedBy { it.second }.map { it.first }\n    }\n\n    private fun findTestFilesForClass(phpClass: PhpClass): List<PsiFile> {\n        return FileBasedIndex.getInstance().getAllKeys(\n          key,\n          phpClass.project\n        ).filter { testClassName -> testClassName.contains(phpClass.name) }\n            .flatMap { testClassName ->\n                FileBasedIndex.getInstance().getContainingFiles(\n                  key,\n                  testClassName,\n                  GlobalSearchScope.projectScope(phpClass.project)\n                )\n            }\n            .mapNotNull { testFile -> phpClass.manager.findFile(testFile) }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/goto/PestTestGoToSymbolContributor.kt",
    "content": "package com.pestphp.pest.goto\n\nimport com.intellij.ide.projectView.PresentationData\nimport com.intellij.navigation.ChooseByNameContributor\nimport com.intellij.navigation.ItemPresentation\nimport com.intellij.navigation.NavigationItem\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.jetbrains.php.PhpPresentationUtil\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.pestphp.pest.PestIcons\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\nimport com.pestphp.pest.indexers.key\n\n/**\n * Adds support for navigating to pest tests via the symbol searching\n */\nclass PestTestGoToSymbolContributor : ChooseByNameContributor {\n    override fun getNames(project: Project, includeNonProjectItems: Boolean): Array<String> {\n        val index = FileBasedIndex.getInstance()\n\n        return index\n            .getAllKeys(key, project)\n            .flatMap {\n                index.getValues(\n                  key,\n                  it,\n                  when {\n                        includeNonProjectItems -> ProjectScope.getAllScope(project)\n                        else -> ProjectScope.getProjectScope(project)\n                    }\n                )\n            }\n            .flatten()\n            .distinct()\n            .toTypedArray()\n    }\n\n    override fun getItemsByName(\n        name: String,\n        pattern: String,\n        project: Project,\n        includeNonProjectItems: Boolean\n    ): Array<NavigationItem> {\n        val index = FileBasedIndex.getInstance()\n        val psiManager = PsiManager.getInstance(project)\n\n        return index\n            .getAllKeys(key, project)\n            .flatMap { fileName ->\n                val hasName = index.getValues(\n                  key,\n                  fileName,\n                  when {\n                        includeNonProjectItems -> ProjectScope.getAllScope(project)\n                        else -> ProjectScope.getProjectScope(project)\n                    }\n                ).flatten()\n                    .contains(name)\n\n                if (!hasName) {\n                    return@flatMap emptyList()\n                }\n\n                index.getContainingFiles(\n                  key,\n                  fileName,\n                  when {\n                        includeNonProjectItems -> ProjectScope.getAllScope(project)\n                        else -> ProjectScope.getProjectScope(project)\n                    }\n                )\n            }.mapNotNull { psiManager.findFile(it) }\n            .flatMap { it.getPestTests() }\n            .filter { it.getPestTestName().equals(name) }\n            .map { functionReference ->\n                val location = PhpPresentationUtil.getPresentablePathForFile(\n                    functionReference.containingFile.virtualFile,\n                    functionReference.project\n                )\n                val presentation = PresentationData(\n                    functionReference.getPestTestName(),\n                    location,\n                    PestIcons.Logo,\n                    null,\n                )\n                PestTestFunctionReference(functionReference, presentation)\n            }\n            .toTypedArray()\n    }\n\n    class PestTestFunctionReference(private val functionReference: FunctionReference,\n                                    private val presentation: ItemPresentation) : NavigationItem {\n        override fun getPresentation(): ItemPresentation = presentation\n\n        override fun navigate(requestFocus: Boolean) = functionReference.navigate(requestFocus)\n\n        override fun canNavigate(): Boolean = functionReference.canNavigate()\n\n        override fun canNavigateToSource(): Boolean = canNavigateToSource()\n\n        override fun getName(): String? {\n            return functionReference.getPestTestName()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/indexers/PestTestIndex.kt",
    "content": "package com.pestphp.pest.indexers\n\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.indexing.DataIndexer\nimport com.intellij.util.indexing.DefaultFileTypeSpecificInputFilter\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.intellij.util.indexing.FileBasedIndexExtension\nimport com.intellij.util.indexing.FileContent\nimport com.intellij.util.indexing.ID\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.intellij.util.io.KeyDescriptor\nimport com.jetbrains.php.lang.PhpFileType\nimport com.jetbrains.php.lang.psi.stubs.indexes.StringSetDataExternalizer\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\nimport com.pestphp.pest.isPestTestFile\nimport com.pestphp.pest.realPath\n\nval key = ID.create<String, Set<String>>(\"php.pest\")\n\n/**\n * Indexes all pest test files with the following key-value store\n * `path/pest-test-file-name => ['it test', 'it should work']`\n * Note that php files with pest-like named functions are indexed as well\n */\nclass PestTestIndex : FileBasedIndexExtension<String, Set<String>>() {\n    override fun getName(): ID<String, Set<String>> {\n        return key\n    }\n\n    override fun getVersion(): Int {\n        return 5\n    }\n\n    override fun dependsOnFileContent(): Boolean {\n        return true\n    }\n\n    override fun getIndexer(): DataIndexer<String, Set<String>, FileContent> {\n        return DataIndexer { inputData ->\n            val file = inputData.psiFile\n\n            if (!file.isPestTestFile()) {\n                return@DataIndexer mapOf()\n            }\n\n            val map = HashMap<String, Set<String>>()\n            map[file.realPath] = file.getPestTests()\n                .mapNotNull { it.getPestTestName() }\n                .toSet()\n            return@DataIndexer map\n        }\n    }\n\n    override fun getInputFilter(): FileBasedIndex.InputFilter {\n      return object : DefaultFileTypeSpecificInputFilter(PhpFileType.INSTANCE) {\n        override fun acceptInput(file: VirtualFile): Boolean {\n          return super.acceptInput(file) && file.path.lowercase().contains(\"test\")\n        }\n      }\n    }\n\n    override fun getKeyDescriptor(): KeyDescriptor<String> {\n        return EnumeratorStringDescriptor.INSTANCE\n    }\n\n    override fun getValueExternalizer(): DataExternalizer<Set<String>> {\n        return StringSetDataExternalizer.INSTANCE\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/ChangeMultipleExpectCallsToChainableQuickFix.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.LocalQuickFix\nimport com.intellij.codeInspection.ProblemDescriptor\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.php.lang.psi.PhpPsiElementFactory\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.features.customExpectations.isExpectation\n\nclass ChangeMultipleExpectCallsToChainableQuickFix : LocalQuickFix {\n    override fun getFamilyName(): String {\n        return PestBundle.message(\"QUICK_FIX_CHANGE_MULTIPLE_EXPECT_INTO_CHAIN\")\n    }\n\n    override fun applyFix(project: Project, descriptor: ProblemDescriptor) {\n        var statement = descriptor.psiElement as? Statement ?: return\n        var expectCall = statement.firstPsiChild as? MethodReference ?: return\n        var replaceExpectCall = expectCall\n\n        if (!expectCall.isExpectation()) {\n            return\n        }\n\n        // Find the first expect call in the group\n        while ((statement.prevPsiSibling as? Statement)?.isExpectation() == true) {\n            statement = statement.prevPsiSibling as Statement\n            expectCall = statement.firstPsiChild as MethodReference\n            replaceExpectCall = expectCall\n        }\n\n        // Loop over all the next statement and merge together to one expect cal..\n        var nextSibling = statement.nextPsiSibling as? Statement\n        while (nextSibling != null) {\n            val siblingMethodReference = nextSibling.firstPsiChild as? MethodReference ?: break\n\n            if (!siblingMethodReference.isExpectation()) {\n                break\n            }\n\n            // Replace expect with and on the next call.\n            replaceExpectCall = PhpPsiElementFactory.createMethodReference(\n                project,\n                replaceExpectCall.text\n                        + \"\\n->\"\n                        + siblingMethodReference.text.replaceFirst(\"expect\", \"and\")\n            )\n\n            val oldSibling = nextSibling\n            nextSibling = nextSibling.nextPsiSibling as? Statement\n            oldSibling.delete()\n        }\n\n        expectCall.replace(replaceExpectCall)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/ChangeTestNameCasingQuickFix.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.LocalQuickFix\nimport com.intellij.codeInspection.ProblemDescriptor\nimport com.intellij.openapi.project.Project\nimport com.intellij.util.text.NameUtilCore\nimport com.jetbrains.php.lang.psi.PhpPsiElementFactory\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.pestphp.pest.PestBundle\n\nfun convertTestNameToSentenceCase(\n    name: String,\n    shouldLowercaseWords: Boolean = true\n) = NameUtilCore.splitNameIntoWordList(name).fold(\"\") { acc, element ->\n    val word = if (shouldLowercaseWords) element.replaceFirstChar(Char::lowercase) else element\n    if (acc.lastOrNull()?.isLetterOrDigit() != true || word.length == 1 && !word[0].isLetterOrDigit())\n        \"$acc$word\"\n    else\n        \"$acc $word\"\n}\n\nfun isInvalidNameCase(name: String) = !name.contains(' ') && convertTestNameToSentenceCase(name, false) != name\n\nclass ChangeTestNameCasingQuickFix : LocalQuickFix {\n    override fun getFamilyName(): String {\n        return PestBundle.message(\"QUICK_FIX_CHANGE_TEST_NAME_CASING\")\n    }\n\n    override fun applyFix(project: Project, descriptor: ProblemDescriptor) {\n        val nameParameter = descriptor.psiElement as? StringLiteralExpression ?: return\n        val pestTestName = nameParameter.contents\n\n        val newNameParameter = PhpPsiElementFactory.createStringLiteralExpression(\n            project,\n            convertTestNameToSentenceCase(pestTestName),\n            true\n        )\n\n        nameParameter.replace(newNameParameter)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/InvalidTestNameCaseInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.psi.PsiElementVisitor\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.getInitialFunctionReference\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.getPestTests\n\nclass InvalidTestNameCaseInspection : PhpInspection() {\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return object : PhpElementVisitor() {\n            override fun visitPhpFile(file: PhpFile) {\n                file.getPestTests()\n                    .groupBy { it.getPestTestName() }\n                    .filterKeys { it != null }\n                    .filterKeys {\n                        // Remove `it ` prefix from test names\n                        val testName = if (it!!.startsWith(\"it \")) it.substring(3) else it\n\n                        isInvalidNameCase(testName)\n                    }\n                    .forEach {\n                        declareProblemType(holder, it.value)\n                    }\n            }\n        }\n    }\n\n    private fun declareProblemType(holder: ProblemsHolder, tests: List<FunctionReference>) {\n        tests\n            .mapNotNull { it.getInitialFunctionReference()?.getParameter(0) }\n            .forEach {\n                holder.registerProblem(\n                    it,\n                    PestBundle.message(\"INSPECTION_INVALID_TEST_NAME_CASE\"),\n                    ChangeTestNameCasingQuickFix()\n                )\n            }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/MissingScreenshotSnapshotInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElementVisitor\nimport com.intellij.psi.util.findParentOfType\nimport com.jetbrains.php.lang.PhpLangUtil\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isPestTestFile\n\nclass MissingScreenshotSnapshotInspection : PhpInspection() {\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return object : PhpElementVisitor() {\n            override fun visitPhpMethodReference(reference: MethodReference) {\n                val methodName = reference.name ?: return\n                if (!PhpLangUtil.equalsMethodNames(methodName, \"assertScreenshotMatches\")) return\n\n                if (!reference.containingFile.isPestTestFile()) return\n\n                val pestCall = reference.findParentOfType<FunctionReferenceImpl>() ?: return\n                val testName = pestCall.getPestTestName() ?: return\n\n                if (!snapshotExists(reference, testName)) {\n                    val namePsi = reference.nameNode?.psi ?: reference\n                    holder.registerProblem(\n                        namePsi,\n                        PestBundle.message(\"INSPECTION_MISSING_SCREENSHOT_SNAPSHOT\")\n                    )\n                }\n            }\n        }\n    }\n\n    private fun snapshotExists(context: MethodReference, testName: String): Boolean {\n        val file = context.containingFile.originalFile.virtualFile ?: return false\n        val testsRoot = getTestsRoot(file) ?: return false\n\n        val relativePath = VfsUtil.getRelativePath(file.parent, testsRoot) ?: return false\n        val snapshotPath = \".pest/snapshots/$relativePath/${file.nameWithoutExtension}\"\n        val expectedDir = testsRoot.findFileByRelativePath(snapshotPath) ?: return false\n\n        val normalizedTestName = testName.replace(\"_\", \"__\").replace(Regex(\"[^a-zA-Z0-9→]\"), \"_\")\n        return expectedDir.children?.any { it.name.matches(\"${normalizedTestName}(__\\\\d+)?\\\\.snap\".toRegex()) } == true\n    }\n\n    private fun getTestsRoot(file: VirtualFile): VirtualFile? {\n        var currentDir = file.parent\n        while (currentDir != null && currentDir.name != \"tests\") {\n            currentDir = currentDir.parent\n        }\n        return currentDir\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/MultipleExpectChainableInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.psi.PsiElementVisitor\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.psi.elements.GroupStatement\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.features.customExpectations.isExpectation\n\nclass MultipleExpectChainableInspection : PhpInspection() {\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return object : PhpElementVisitor() {\n            override fun visitPhpGroupStatement(groupStatement: GroupStatement) {\n                var counter = 1\n                groupStatement.statements\n                    .filterIsInstance(Statement::class.java)\n                    .groupBy {\n                        val methodReference = it.firstPsiChild as? MethodReference\n                        if (methodReference?.text?.startsWith(\"expect\") != true || !methodReference.type.isExpectation(holder.project)) {\n                            counter++\n                            return@groupBy 0\n                        }\n                        counter\n                    }\n                    .toMutableMap()\n                    // Drop index 0, as that is all non expect calls\n                    .also { it.remove(0) }\n                    // Filter all expect call groups with only one expect call\n                    .filterValues { it.size >= 2 }\n                    .forEach {\n                        declareProblemType(holder, it.value)\n                    }\n            }\n\n        }\n    }\n\n    @Suppress(\"SpreadOperator\")\n    private fun declareProblemType(holder: ProblemsHolder, statements: List<Statement>) {\n        statements\n            .forEach {\n                holder.registerProblem(\n                    it,\n                    PestBundle.message(\"INSPECTION_MULTIPLE_CHAINABLE_EXPECT_CALLS\"),\n                    ChangeMultipleExpectCallsToChainableQuickFix()\n                )\n            }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/PestAssertionCanBeSimplifiedInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.modcommand.ActionContext\nimport com.intellij.modcommand.ModPsiUpdater\nimport com.intellij.modcommand.PsiUpdateModCommandAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.openapi.util.text.StringUtil.capitalize\nimport com.intellij.openapi.util.text.StringUtil.toLowerCase\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiElementVisitor\nimport com.intellij.psi.SmartPointerManager\nimport com.intellij.refactoring.suggested.endOffset\nimport com.intellij.refactoring.suggested.startOffset\nimport com.jetbrains.php.lang.PhpLangUtil\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.inspections.probablyBug.PhpDivisionByZeroInspection\nimport com.jetbrains.php.lang.lexer.PhpTokenTypes\nimport com.jetbrains.php.lang.parser.PhpElementTypes\nimport com.jetbrains.php.lang.psi.PhpPsiElementFactory\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.PhpTypedElement\nimport com.jetbrains.php.lang.psi.elements.impl.ParameterListImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.PestBundle\n\ninternal class PestAssertionCanBeSimplifiedInspection : PhpInspection() {\n  override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n    return object : PhpElementVisitor() {\n      override fun visitPhpMethodReference(reference: MethodReference) {\n        val methodNamePsi = reference.nameNode?.psi ?: return\n\n        getMainParameterFromToBe(reference, methodNamePsi)?.let { mainParameter ->\n          registerProblem(methodNamePsi, mainParameter, \"toBe${capitalize(toLowerCase(mainParameter.text))}\")\n        }\n        getMainParameterFromToHaveCount(reference, methodNamePsi)?.let { mainParameter ->\n          registerProblem(methodNamePsi, mainParameter, \"toBeEmpty\")\n        }\n      }\n\n      private fun getMainParameterFromToBe(reference: MethodReference, methodNameIdentifier: PsiElement): PsiElement? {\n        val parameter = reference.parameterList?.getParameter(\"count\", 0) ?: return null\n        if (PhpLangUtil.equalsMethodNames(methodNameIdentifier.text, \"toBe\") &&\n            (PhpLangUtil.isTrue(parameter) || PhpLangUtil.isFalse(parameter) || PhpLangUtil.isNull(parameter))) {\n          return parameter\n        }\n        return null\n      }\n\n      private fun getMainParameterFromToHaveCount(reference: MethodReference, methodNameIdentifier: PsiElement): PsiElement? {\n        val parameter = reference.parameterList?.getParameter(\"expected\", 0) ?: return null\n        if (PhpLangUtil.equalsMethodNames(methodNameIdentifier.text, \"toHaveCount\") &&\n            PhpPsiUtil.isOfType(parameter, PhpElementTypes.NUMBER) &&\n            PhpDivisionByZeroInspection.isZero(parameter)) {\n          val functionCall = reference.classReference as? FunctionReference\n          if (functionCall == null || functionCall.parameters.size != 1) return null\n          val functionName = functionCall.name\n          val functionParameter = functionCall.parameters.first()\n          if (functionName == \"expect\" && functionParameter is PhpTypedElement && PhpType.isArray(functionParameter.globalType)) {\n            return parameter\n          }\n        }\n        return null\n      }\n\n      private fun registerProblem(methodNamePsi: PsiElement,\n                                  parameterToRemove: PsiElement,\n                                  newMethodName: String) {\n        holder.problem(methodNamePsi, PestBundle.message(\"INSPECTION_ASSERTION_CAN_BE_SIMPLIFIED\", methodNamePsi.text, newMethodName))\n          .fix(PestSimplifyAssertionQuickFix(newMethodName, methodNamePsi, parameterToRemove))\n          .register()\n      }\n    }\n  }\n\n  private class PestSimplifyAssertionQuickFix(\n    private val newMethodName: String,\n    methodNamePsi: PsiElement,\n    parameterToRemove: PsiElement\n  ) : PsiUpdateModCommandAction<PsiElement>(methodNamePsi) {\n    private val parameterToRemovePointer = SmartPointerManager.getInstance(parameterToRemove.getProject())\n      .createSmartPsiElementPointer(parameterToRemove)\n\n    override fun getFamilyName() = PestBundle.message(\"QUICK_FIX_SIMPLIFY_ASSERTION\")\n\n    override fun invoke(context: ActionContext, methodNamePsi: PsiElement, updater: ModPsiUpdater) {\n      val parameterToRemove = updater.getWritable(parameterToRemovePointer.element) ?: return\n      val methodReference = methodNamePsi.parent as? MethodReference\n      if (methodReference == null) return\n\n      val methodEnd = PhpPsiUtil.findNextSiblingOfAnyType(methodNamePsi, PhpTokenTypes.chRPAREN) ?: return\n      (methodReference.parameterList as? ParameterListImpl)?.removeParameter(parameterToRemove)\n      val newMethodCallText = \"$newMethodName(${methodReference.parameterList?.text})\"\n      val newMethodReference = insertIntoMethodReference(methodReference,\n                                                         TextRange(methodNamePsi.startOffset, methodEnd.endOffset),\n                                                         newMethodCallText,\n                                                         context.project)\n      methodReference.replace(newMethodReference)\n    }\n\n    private fun insertIntoMethodReference(methodReference: MethodReference,\n                                          insertionRange: TextRange,\n                                          insertionText: String,\n                                          project: Project): MethodReference {\n      val referenceRange = methodReference.textRange\n      val referenceText = methodReference.text\n      val referenceRelativeStart = insertionRange.startOffset - referenceRange.startOffset\n      val referenceRelativeEnd = insertionRange.endOffset - referenceRange.startOffset\n      val newMethodReferenceText = referenceText.substring(0, referenceRelativeStart) +\n                                   insertionText +\n                                   referenceText.substring(referenceRelativeEnd)\n      return PhpPsiElementFactory.createMethodReference(project, newMethodReferenceText)\n    }\n  }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/PestTestFailedLineInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.psi.PsiElementVisitor\nimport com.jetbrains.php.lang.inspections.PhpTestFailedLineInspectionBase\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.visitors.PhpElementVisitor\nimport com.pestphp.pest.isPestTestReference\nimport com.pestphp.pest.runner.PestFailedLineManager\n\nclass PestTestFailedLineInspection : PhpTestFailedLineInspectionBase() {\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return object : PhpElementVisitor() {\n            override fun visitPhpFunctionCall(functionCall: FunctionReference) {\n                if (!functionCall.isPestTestReference()) return\n                val failedLineManager = holder.project.getService(PestFailedLineManager::class.java)\n                process(holder, functionCall, failedLineManager)\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/SuppressExpressionResultUnusedInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.InspectionSuppressor\nimport com.intellij.codeInspection.SuppressQuickFix\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.inspections.PhpExpressionResultUnusedInspection\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.isPestTestFunction\n\nclass SuppressExpressionResultUnusedInspection : InspectionSuppressor {\n    companion object {\n        private val SUPPRESSED_PHP_INSPECTIONS = listOf(PhpExpressionResultUnusedInspection().id)\n    }\n\n    override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean {\n        if (element !is FunctionReferenceImpl) {\n            return false\n        }\n\n        if (!element.isPestTestFunction()) {\n            return false\n        }\n\n        return SUPPRESSED_PHP_INSPECTIONS.contains(toolId)\n    }\n\n    override fun getSuppressActions(element: PsiElement?, toolId: String): Array<SuppressQuickFix> {\n        return SuppressQuickFix.EMPTY_ARRAY\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/inspections/SuppressUndefinedPropertyInspection.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.codeInspection.InspectionSuppressor\nimport com.intellij.codeInspection.SuppressQuickFix\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.inspections.PhpDynamicFieldDeclarationInspection\nimport com.jetbrains.php.lang.inspections.PhpUndefinedFieldInspection\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.pestphp.pest.isAnyPestFunction\nimport com.pestphp.pest.isThisVariableInPest\n\nclass SuppressUndefinedPropertyInspection : InspectionSuppressor {\n    private val suppressedPhpInspections = listOf(PhpUndefinedFieldInspection().id, PhpDynamicFieldDeclarationInspection().id)\n\n    override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean {\n        if (!suppressedPhpInspections.contains(toolId)) {\n            return false\n        }\n\n        val fieldReference = element.parent as? FieldReference ?: return false\n\n        if (!fieldReference.classReference.isThisVariableInPest { it.isAnyPestFunction() }) {\n            return false\n        }\n\n        return suppressedPhpInspections.contains(toolId)\n    }\n\n    override fun getSuppressActions(element: PsiElement?, toolId: String): Array<SuppressQuickFix> {\n        return SuppressQuickFix.EMPTY_ARRAY\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/notifications/OutdatedNotification.kt",
    "content": "package com.pestphp.pest.notifications\n\nimport com.intellij.notification.Notification\nimport com.intellij.notification.NotificationGroupManager\nimport com.intellij.notification.NotificationType\nimport com.intellij.openapi.project.Project\nimport org.jetbrains.annotations.Nls\n\nclass OutdatedNotification {\n    private val group = NotificationGroupManager.getInstance()\n        .getNotificationGroup(\"Outdated Pest\")\n\n    fun notify(project: Project?, @Nls content: String): Notification {\n        val notification: Notification = group.createNotification(content, NotificationType.ERROR)\n        notification.notify(project)\n        return notification\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/parser/PestConfigurationFile.kt",
    "content": "package com.pestphp.pest.parser\n\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\n\ndata class PestConfigurationFile(\n    val baseTestType: PhpType,\n    val pathsClasses: List<Pair<String, PhpType>>\n)\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/parser/PestConfigurationFileParser.kt",
    "content": "package com.pestphp.pest.parser\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.Key\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileManager\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.PsiRecursiveElementWalkingVisitor\nimport com.intellij.psi.util.CachedValue\nimport com.intellij.psi.util.CachedValueProvider\nimport com.intellij.psi.util.CachedValuesManager\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.elements.impl.PhpFilePathUtils\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.CONFIGURATION_FUNCTIONS\nimport com.pestphp.pest.PestSettings\nimport com.pestphp.pest.features.configuration.getConfigurationFunctionCall\nimport com.pestphp.pest.getBaseDir\nimport com.pestphp.pest.getConfigurationPhpType\nimport com.pestphp.pest.getPestConfigurationPhpType\nimport kotlin.io.path.Path\n\nclass PestConfigurationFileParser(private val settings: PestSettings) {\n    fun parse(project: Project, virtualFile: VirtualFile? = null): PestConfigurationFile {\n        val projectDir = project.guessProjectDir() ?: return defaultConfig\n        // Use the location of the composer.json file or the project dir\n        val baseDir = getBaseDir(project, virtualFile) ?: return defaultConfig\n\n        val pestFile = VirtualFileManager.getInstance().findFileByUrl(baseDir.url + \"/\" + settings.pestFilePath)\n            ?: return defaultConfig\n\n        val psiFile = PsiManager.getInstance(project).findFile(pestFile) as? PhpFile ?: return defaultConfig\n\n        return CachedValuesManager.getCachedValue(psiFile, cacheKey) {\n            var baseType = PhpType().add(\"\\\\PHPUnit\\\\Framework\\\\TestCase\")\n            val inPaths = mutableListOf<Pair<String, PhpType>>()\n            val relativePath = Path(projectDir.path).relativize(Path(baseDir.path)).toString().run {\n                if (this.isBlank()) this else \"$this/\"\n            }\n            val testsPath = relativePath + settings.pestFilePath.replaceAfterLast(\"/\", \"\", \"\")\n\n            psiFile.acceptChildren(\n                Visitor { type, inPath, fullPath ->\n                    if (fullPath && inPath != null) {\n                        inPaths.add(Pair(inPath.replaceBefore(testsPath, \"\"), type))\n                    } else if (inPath != null) {\n                        inPaths.add(Pair(testsPath + inPath, type))\n                    } else {\n                        baseType = type\n                    }\n                }\n            )\n\n            CachedValueProvider.Result.create(PestConfigurationFile(baseType, inPaths), psiFile)\n        } ?: defaultConfig\n    }\n\n    private class Visitor(private val collect: (PhpType, String?, Boolean) -> Unit) :\n        PsiRecursiveElementWalkingVisitor() {\n        override fun visitElement(element: PsiElement) {\n            if (element is MethodReference) {\n                if (element.name == \"in\") {\n                    visitInReference(element)\n                } else if (getConfigurationFunctionCall(element)?.name in CONFIGURATION_FUNCTIONS) {\n                    collect(element.getPestConfigurationPhpType() ?: return,\n                            if (getConfigurationFunctionCall(element)?.name == \"pest\" &&\n                                element.containingFile.name == CONFIGURATION_FILE_NAME) DEFAULT_DIRECTORY else null,\n                            false)\n                }\n                return\n            } else if (element is FunctionReferenceImpl) {\n                if (element.name == \"uses\") {\n                    collect(element.getConfigurationPhpType() ?: return, null, false)\n                }\n                return\n            }\n\n            super.visitElement(element)\n        }\n\n        private fun visitInReference(inReference: MethodReference) {\n            var reference = inReference\n            var usesType: PhpType? = null\n            while (true) {\n                val ref = reference.classReference ?: return\n\n                if (ref is MethodReference) {\n                    reference = ref\n                } else if (ref is FunctionReferenceImpl) {\n                    if (ref.name in CONFIGURATION_FUNCTIONS) {\n                        usesType = (inReference.classReference as? FunctionReference)?.getPestConfigurationPhpType()\n                    }\n\n                    break\n                } else {\n                    return\n                }\n            }\n\n            if (usesType == null) return\n\n            inReference.parameters\n                .map {\n                    PhpFilePathUtils.getStaticPath(it as PhpPsiElement?)\n                }\n                .forEach {\n                    collect(usesType, it, false)\n                }\n        }\n    }\n\n    companion object {\n        private val defaultConfig = PestConfigurationFile(\n            PhpType().add(\"\\\\PHPUnit\\\\Framework\\\\TestCase\"),\n            emptyList()\n        )\n\n        private val cacheKey = Key<CachedValue<PestConfigurationFile>>(\"com.pestphp.pest_configuration\")\n\n        private const val DEFAULT_DIRECTORY = \"\"\n        private const val CONFIGURATION_FILE_NAME = \"Pest.php\"\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/runner/LocationInfo.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.openapi.vfs.VirtualFile\n\nclass LocationInfo(\n    val file: VirtualFile,\n    val testName: String?\n)\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/runner/PestConsoleProperties.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.execution.Executor\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.impl.ConsoleViewImpl\nimport com.intellij.execution.testframework.Printer\nimport com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerEventsListener\nimport com.intellij.execution.testframework.sm.runner.SMTestLocator\nimport com.intellij.execution.testframework.sm.runner.SMTestProxy\nimport com.intellij.execution.testframework.sm.runner.ui.TestStackTraceParser\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.project.Project\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.configuration.PestLocationProvider\nimport com.pestphp.pest.configuration.PestRerunFailedTestsAction\nimport com.pestphp.pest.features.parallel.PestParallelSMTEventsAdapter\nimport com.pestphp.pest.features.parallel.PestParallelTestExecutor\nimport com.pestphp.pest.features.parallel.executeInParallel\n\nclass PestConsoleProperties(\n    config: RunConfiguration,\n    executor: Executor,\n    private val testLocator: PestLocationProvider\n) : SMTRunnerConsoleProperties(config, PestBundle.message(\"FRAMEWORK_NAME\"), executor) {\n\n    init {\n        if (executor is PestParallelTestExecutor || executeInParallel(config)) {\n            config.project.messageBus.connect(this)\n                .subscribe(SMTRunnerEventsListener.TEST_STATUS, PestParallelSMTEventsAdapter())\n        }\n    }\n\n    override fun getTestLocator(): SMTestLocator {\n        return testLocator\n    }\n\n    override fun createRerunFailedTestsAction(consoleView: ConsoleView?): AbstractRerunFailedTestsAction? {\n        return consoleView?.let { PestRerunFailedTestsAction(it, this) }\n    }\n\n    override fun isPrintTestingStartedTime(): Boolean {\n        return false\n    }\n\n    override fun printExpectedActualHeader(printer: Printer, expected: String, actual: String) {\n        super.printExpectedActualHeader(printer, expected, actual)\n    }\n\n    override fun createConsole(): ConsoleView {\n        return super.createConsole() as ConsoleViewImpl\n    }\n\n    override fun getTestStackTraceParser(url: String, proxy: SMTestProxy, project: Project): TestStackTraceParser {\n        return parse(url, proxy.stacktrace, proxy.errorMessage, testLocator, project)\n    }\n\n    @Deprecated(\"Deprecated in Java\", ReplaceWith(\"true\"))\n    override fun serviceMessageHasNewLinePrefix(): Boolean {\n        return true\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/runner/PestFailedLineManager.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.execution.TestStateStorage\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.fileEditor.FileEditorManagerListener\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.phpunit.PhpUnitTestRunLineMarkerProvider\nimport com.jetbrains.php.phpunit.PhpUnitTestRunLineMarkerProvider.createPathMapper\nimport com.jetbrains.php.testFramework.PhpTestFrameworkFailedLineManager\nimport com.pestphp.pest.configuration.PestLocationProvider\nimport com.pestphp.pest.features.datasets.isDatasetCall\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.withoutFirstFileSeparator\n\n@Service(Service.Level.PROJECT)\nclass PestFailedLineManager(\n    project: Project\n) : PhpTestFrameworkFailedLineManager(project), FileEditorManagerListener {\n    override fun getTestLocationUrl(testElement: PsiElement): String? {\n        if (testElement !is FunctionReference) return null\n        return getLocationUrl(testElement.containingFile, testElement)\n    }\n\n    override fun getRecordsForTest(testElement: PsiElement): List<TestStateStorage.Record> {\n        val testLocationUrl = getTestLocationUrl(testElement) ?: return emptyList()\n        val testStateRecord = TestStateStorage.getInstance(testElement.project).getState(testLocationUrl) ?: return emptyList()\n\n        val project = testElement.getProject()\n        val records = mutableListOf(testStateRecord)\n        if (testStateRecord.failedLine == -1 && (testElement.parent as? MethodReference)?.isDatasetCall() == true) {\n            val allRecordLocationUrls = TestStateStorage.getInstance(project).keys\n            val dataSetRecords: List<TestStateStorage.Record> = allRecordLocationUrls\n                .asSequence()\n                .filterNotNull()\n                .filter { recordLocationUrl -> isLocationUrlWithNamedDatasetValue(recordLocationUrl, testLocationUrl) }\n                .map { recordLocationUrl -> TestStateStorage.getInstance(project).getState(recordLocationUrl) }\n                .filterNotNull()\n                .filter { record -> record.failedLine != -1 }\n                .toList()\n\n            records.addAll(dataSetRecords)\n        }\n        return records\n    }\n\n    private fun isLocationUrlWithNamedDatasetValue(recordLocationUrl: String, testLocationUrl: String): Boolean =\n        recordLocationUrl.startsWith(\"$testLocationUrl with data set \\\"dataset\")\n\n    private fun getLocationUrl(containingFile: PsiFile, functionCall: FunctionReference): String =\n        getLocationUrl(containingFile) + \"::\" + functionCall.getPestTestName()\n}\n\ninternal fun getLocationUrl(psiFile: PsiFile): String {\n    return \"${PestLocationProvider.PROTOCOL_ID}://${\n        PhpUnitTestRunLineMarkerProvider.getFilePathDeploymentAware(psiFile)\n            .removePrefix(getProjectPathDeploymentAware(psiFile.project)).withoutFirstFileSeparator\n    }\"\n}\n\nprivate fun getProjectPathDeploymentAware(project: Project): String {\n    val projectPath = project.basePath ?: return \"\"\n    val remoteMapper = createPathMapper(project)\n    return if (remoteMapper.canProcess(projectPath)) {\n        remoteMapper.process(projectPath)\n    } else {\n        projectPath\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/runner/PestPressToContinueAction.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.execution.impl.ConsoleViewImpl\nimport com.intellij.execution.testframework.ui.BaseTestsOutputConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.LangDataKeys\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.util.TextRange\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.configuration.PestRunConfigurationType\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\n\nclass PestPressToContinueAction : DumbAwareAction() {\n\n    override fun getActionUpdateThread(): ActionUpdateThread {\n        return ActionUpdateThread.BGT\n    }\n\n    override fun update(e: AnActionEvent) {\n        val descriptor = e.getData(LangDataKeys.RUN_CONTENT_DESCRIPTOR)\n        val processHandler = descriptor?.processHandler\n        e.presentation.setText(PestBundle.messagePointer(\"action.press.to.continue.text\"))\n        e.presentation.isVisible = descriptor?.runConfigurationTypeId == PestRunConfigurationType.instance.id\n        e.presentation.isEnabled = processHandler != null &&\n            !processHandler.isProcessTerminated &&\n            !processHandler.isProcessTerminating &&\n            getInnerConsoleViewImpl(descriptor)?.let { shouldEnableAndPrintHint(it) } == true\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val descriptor = e.getData(LangDataKeys.RUN_CONTENT_DESCRIPTOR) ?: return\n        val processHandler = descriptor.processHandler ?: return\n        val processInput = processHandler.processInput ?: return\n        processInput.let { stream ->\n            try {\n                stream.write(\"\\n\".toByteArray(StandardCharsets.UTF_8))\n                stream.flush()\n            } catch (io: IOException) {\n                logger.warn(\"Failed to write to process stdin for Pest Press to continue\", io)\n            }\n        }\n    }\n\n    private fun getInnerConsoleViewImpl(descriptor: RunContentDescriptor): ConsoleViewImpl? {\n        val baseConsole = descriptor.executionConsole as? BaseTestsOutputConsoleView\n        return baseConsole?.console as? ConsoleViewImpl\n    }\n\n    private fun readLastNonEmptyLineOrEmpty(view: ConsoleViewImpl): String {\n        val editor = view.editor ?: return \"\"\n        val doc = editor.document\n        var line = doc.lineCount - 1\n        while (line >= 0) {\n            val start = doc.getLineStartOffset(line)\n            val end = doc.getLineEndOffset(line)\n            val text = doc.getText(TextRange(start, end))\n            if (text.any { !it.isWhitespace() }) {\n                return text\n            }\n            line--\n        }\n        return \"\"\n    }\n\n    private fun shouldEnableAndPrintHint(view: ConsoleViewImpl): Boolean {\n        val line = readLastNonEmptyLineOrEmpty(view)\n        if (line.contains(PROMPT)) {\n            view.print(\"\\n  $HINT\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n            return true\n        }\n        return line.contains(HINT)\n    }\n\n    internal companion object {\n        private val logger = Logger.getInstance(PestPressToContinueAction::class.java)\n        internal const val PROMPT: String = \"Press any key to continue\"\n        internal const val HINT: String = \"To continue, click \\\"Continue Test Run\\\" on the test results' toolbar.\"\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/runner/PestPromptConsoleFolding.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.execution.ConsoleFolding\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.ide.DataManager\nimport com.intellij.openapi.actionSystem.LangDataKeys\nimport com.intellij.openapi.project.Project\nimport com.pestphp.pest.configuration.PestRunConfigurationType\n\nclass PestPromptConsoleFolding : ConsoleFolding() {\n\n    override fun isEnabledForConsole(consoleView: ConsoleView): Boolean {\n        val context = DataManager.getInstance().getDataContext(consoleView.component)\n        val descriptor = context.getData(LangDataKeys.RUN_CONTENT_DESCRIPTOR) ?: return false\n        return descriptor.runConfigurationTypeId == PestRunConfigurationType.instance.id\n    }\n\n    override fun shouldFoldLine(project: Project, line: String): Boolean {\n        return line.contains(PestPressToContinueAction.PROMPT)\n    }\n\n    override fun getPlaceholderText(project: Project, lines: List<String>): String {\n        return \"\"\n    }\n\n    override fun shouldBeAttachedToThePreviousLine(): Boolean {\n        return false\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/runner/PestTestStackTraceParser.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.execution.testframework.sm.runner.ui.TestStackTraceParser\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiManager\nimport com.intellij.util.DocumentUtil\nimport com.jetbrains.php.util.pathmapper.PhpLocalPathMapper\nimport com.pestphp.pest.configuration.PestLocationProvider\n\nfun parse(url: String,\n          stacktrace: String?,\n          errorMessage: String?,\n          locator: PestLocationProvider,\n          project: Project): PestTestStackTraceParser {\n    if (stacktrace == null) return PestTestStackTraceParser(errorMessage)\n    val lines = stacktrace.split(\"\\n\")\n    if (lines.isEmpty()) return PestTestStackTraceParser(errorMessage)\n    val realErrorMessage = if (errorMessage.isNullOrEmpty()) lines[0] else errorMessage\n\n    val path = url.removePrefix(\"${PestLocationProvider.PROTOCOL_ID}://\").substringBefore( \"::\")\n    val lastLine = lines.last().trim { it <= ' ' }.substringAfter(\"at \")\n    if (path == url || !lastLine.startsWith(path)) return PestTestStackTraceParser(realErrorMessage)\n    val failedLine = StringUtil.parseInt(lastLine.substring(path.length + 1), -1)\n    val failedLineText = if (failedLine > 0) getLineText(path, failedLine, project, locator) else null\n    return PestTestStackTraceParser(failedLine, failedLineText, realErrorMessage, null)\n}\n\nprivate fun getLineText(\n    path: String,\n    line: Int,\n    project: Project,\n    locator: PestLocationProvider\n): String? {\n    val fileUrl = locator.calculateFileUrl(path)\n    val vFile = locator.pathMapper.getLocalFile(fileUrl) ?: PhpLocalPathMapper(project).getLocalFile(fileUrl) ?: return null\n    val psiFile = PsiManager.getInstance(project).findFile(vFile) ?: return null\n    val document = PsiDocumentManager.getInstance(project).getDocument(psiFile) ?: return null\n    if (line > document.lineCount) return null\n    val range = DocumentUtil.getLineTextRange(document, line - 1)\n    return document.getText(range)\n}\n\nclass PestTestStackTraceParser(\n    failedLine: Int,\n    failedMethodName: String?,\n    errorMessageName: String?,\n    topLocationLine: String?,\n) : TestStackTraceParser(failedLine, failedMethodName, errorMessageName, topLocationLine) {\n    constructor(errorMessage: String?) : this(-1, null, errorMessage, null)\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/statistics/PestUsagesCollector.kt",
    "content": "package com.pestphp.pest.statistics\n\nimport com.intellij.internal.statistic.eventLog.EventLogGroup\nimport com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector\nimport com.intellij.openapi.project.Project\n\nobject PestUsagesCollector : CounterUsagesCollector() {\n    private val GROUP = EventLogGroup(\"pest\", 2)\n    private val PEST_MUTATION_TEST_EXECUTED = GROUP.registerEvent(\"pest.mutation.test.executed\")\n    private val PEST_PARALLEL_TEST_EXECUTED = GROUP.registerEvent(\"pest.parallel.test.executed\")\n\n    override fun getGroup(): EventLogGroup = GROUP\n\n    fun logMutationTestExecution(project: Project) {\n        PEST_MUTATION_TEST_EXECUTED.log(project)\n    }\n\n    fun logParallelTestExecution(project: Project) {\n        PEST_PARALLEL_TEST_EXECUTED.log(project)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/structureView/PestStructureViewElement.kt",
    "content": "package com.pestphp.pest.structureView\n\nimport com.intellij.ide.projectView.PresentationData\nimport com.intellij.ide.structureView.StructureViewTreeElement\nimport com.intellij.ide.util.treeView.smartTree.TreeElement\nimport com.intellij.navigation.ItemPresentation\nimport com.intellij.psi.NavigatablePsiElement\nimport com.pestphp.pest.PestIcons\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isPestTestReference\n\n/**\n * Defines how the elements in the structure view\n * should be rendered.\n */\nclass PestStructureViewElement(val element: NavigatablePsiElement) : StructureViewTreeElement {\n    override fun getPresentation(): ItemPresentation {\n        if (!element.isPestTestReference()) {\n            return element.presentation ?: PresentationData()\n        }\n\n        return PresentationData(\n            element.getPestTestName(),\n            null,\n            PestIcons.Logo,\n            null,\n        )\n    }\n\n    override fun getChildren(): Array<TreeElement> {\n        return arrayOf()\n    }\n\n    override fun navigate(requestFocus: Boolean) {\n        return element.navigate(requestFocus)\n    }\n\n    override fun canNavigate(): Boolean {\n        return element.canNavigate()\n    }\n\n    override fun canNavigateToSource(): Boolean {\n        return element.canNavigateToSource()\n    }\n\n    override fun getValue(): Any {\n        return element\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/structureView/PestStructureViewExtension.kt",
    "content": "package com.pestphp.pest.structureView\n\nimport com.intellij.ide.structureView.StructureViewExtension\nimport com.intellij.ide.structureView.StructureViewTreeElement\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.pestphp.pest.getPestTests\n\n/**\n * Extends the structure view, so we can include all\n * the pest tests in it.\n */\nclass PestStructureViewExtension : StructureViewExtension {\n    override fun getType(): Class<out PsiElement> {\n        return PhpFile::class.java\n    }\n\n    override fun getChildren(parent: PsiElement?): Array<StructureViewTreeElement> {\n        if (parent !is PhpFile) {\n            return arrayOf()\n        }\n        return parent.getPestTests()\n            .map { PestStructureViewElement(it) }\n            .toTypedArray()\n    }\n\n    override fun getCurrentEditorElement(editor: Editor?, parent: PsiElement?): Any? {\n        return null\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/surrounders/ExpectStatementSurrounder.kt",
    "content": "package com.pestphp.pest.surrounders\n\nimport com.intellij.lang.surroundWith.Surrounder\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.PhpPsiElementFactory\nimport com.pestphp.pest.PestBundle\n\nclass ExpectStatementSurrounder : Surrounder {\n    override fun getTemplateDescription(): String {\n        return PestBundle.message(\"EXPECT_STATEMENT\")\n    }\n\n    override fun isApplicable(elements: Array<out PsiElement>): Boolean {\n        return true\n    }\n\n    override fun surroundElements(project: Project, editor: Editor, elements: Array<out PsiElement>): TextRange? {\n        val template = PhpPsiElementFactory.createStatement(\n            project,\n            \"expect(${elements.joinToString(\"\") { it.text }})\"\n        )\n\n        val lastElement = elements.last()\n        val replaced = lastElement.replace(template)\n        elements.forEach { it.delete() }\n\n        return replaced.textRange\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/surrounders/StatementSurroundDescriptor.kt",
    "content": "package com.pestphp.pest.surrounders\n\nimport com.intellij.lang.surroundWith.SurroundDescriptor\nimport com.intellij.lang.surroundWith.Surrounder\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.jetbrains.php.surroundWith.PhpStatementSurroundDescriptor\nimport com.pestphp.pest.getPestTests\n\nclass StatementSurroundDescriptor : SurroundDescriptor {\n    override fun getElementsToSurround(file: PsiFile, startOffset: Int, endOffset: Int): Array<PsiElement> {\n        val range = TextRange(startOffset, endOffset)\n\n        val insideTest = file.getPestTests()\n            .any { it.textRange.contains(range) }\n\n        if (!insideTest) {\n            return arrayOf()\n        }\n\n        return PhpStatementSurroundDescriptor().getElementsToSurround(\n            file, startOffset, endOffset\n        )\n    }\n\n    override fun getSurrounders(): Array<Surrounder> {\n        return arrayOf(ExpectStatementSurrounder())\n    }\n\n    override fun isExclusive(): Boolean {\n        return false\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/templates/PestConfigNewDatasetFileAction.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.intellij.ide.actions.CreateFileFromTemplateDialog\nimport com.intellij.ide.fileTemplates.FileTemplate\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiDirectory\nimport com.intellij.psi.PsiFile\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestIcons\n\n\n/**\n * Shows the \"Create Pest Dataset File\" action in the context menu when creating a new file.\n *\n * This will only show if the file is being created in a directory named \"tests\".\n */\nprivate class PestConfigNewDatasetFileAction : PestConfigNewFileAction() {\n    companion object {\n        const val PEST_SHARED_DATASET_TEMPLATE = \"Pest Shared Dataset\"\n        const val PEST_SCOPED_DATASET_TEMPLATE = \"Pest Scoped Dataset\"\n    }\n    override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {\n        builder\n            .setTitle(PestBundle.message(\"CREATE_NEW_PEST_DATASET_DIALOG_TITLE\"))\n            .addKind(PestBundle.message(\"CREATE_NEW_PEST_SHARED_DATASET\"), PestIcons.Dataset, PEST_SHARED_DATASET_TEMPLATE)\n            .addKind(PestBundle.message(\"CREATE_NEW_PEST_SCOPED_DATASET\"), PestIcons.Dataset, PEST_SCOPED_DATASET_TEMPLATE)\n    }\n\n    override fun getActionName(directory: PsiDirectory?, newName: String, templateName: String?): String {\n        return PestBundle.message(\"action.Pest.New.Dataset.text\")\n    }\n\n    override fun createFileFromTemplate(name: String?, template: FileTemplate, dir: PsiDirectory): PsiFile {\n        if (template.name == PEST_SHARED_DATASET_TEMPLATE) {\n            // find parent directory named \"tests\"\n            var parentDir = dir\n            while (parentDir.name != \"tests\") {\n                parentDir = parentDir.parentDirectory ?: break\n            }\n\n            val datasetDir = parentDir.findSubdirectory(\"Datasets\")\n                ?: parentDir.createSubdirectory(\"Datasets\")\n\n            // Check if first character is lowercase in name\n            var newName = name\n            if (name!![0].isLowerCase()) {\n                newName = name.replaceFirstChar { it.uppercase() }\n            }\n\n            return createFileFromTemplate(\n                newName,\n                template,\n                datasetDir,\n                defaultTemplateProperty,\n                true,\n                mapOf(\"DATASET_NAME\" to name.replaceFirstChar { it.lowercase() })\n            )!!\n        }\n\n        return createFileFromTemplate(\n            \"Datasets\",\n            template,\n            dir,\n            defaultTemplateProperty,\n            true,\n            mapOf(\"DATASET_NAME\" to name!!.replaceFirstChar { it.lowercase() })\n        )!!\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/templates/PestConfigNewFileAction.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.intellij.ide.actions.CreateFileFromTemplateAction\nimport com.intellij.ide.actions.CreateFileFromTemplateDialog\nimport com.intellij.ide.fileTemplates.FileTemplate\nimport com.intellij.openapi.actionSystem.DataContext\nimport com.intellij.openapi.actionSystem.LangDataKeys\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiDirectory\nimport com.intellij.psi.PsiFile\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestIcons\nimport com.pestphp.pest.PestSettings\nimport com.pestphp.pest.PestSettings.TestFlavor\n\n\n/**\n * Shows the \"Create Pest Test File\" action in the context menu when creating a new file.\n *\n * This will only show if the file is being created in a directory named \"tests\".\n */\nopen class PestConfigNewFileAction : CreateFileFromTemplateAction() {\n    companion object {\n        const val PEST_IT_TEMPLATE = \"Pest It\"\n        const val PEST_TEST_TEMPLATE = \"Pest Test\"\n    }\n\n    override fun isAvailable(dataContext: DataContext): Boolean {\n        val view = LangDataKeys.IDE_VIEW.getData(dataContext)\n        var psiDir: PsiDirectory? = null\n        if (view != null) {\n            val directories = view.directories\n            if (directories.size == 1) {\n                psiDir = directories[0]\n            }\n        }\n\n        if (psiDir == null || !psiDir.isValid) {\n            return false\n        }\n\n        val virtualDir = psiDir.virtualFile\n        if (!virtualDir.isValid || !virtualDir.isDirectory) {\n            return false\n        }\n\n        return virtualDir.path.contains(\"tests\")\n    }\n\n    override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {\n        builder\n            .setTitle(PestBundle.message(\"CREATE_NEW_PEST_TEST_DIALOG_TITLE\"))\n            .addKind(PestBundle.message(\"CREATE_NEW_PEST_IT_FLAVOR\"), PestIcons.File, PEST_IT_TEMPLATE)\n            .addKind(PestBundle.message(\"CREATE_NEW_PEST_TEST_FLAVOR\"), PestIcons.File, PEST_TEST_TEMPLATE)\n    }\n\n    override fun getActionName(directory: PsiDirectory?, newName: String, templateName: String?): String {\n        return PestBundle.message(\"action.Pest.New.File.text\")\n    }\n\n    override fun createFileFromTemplate(name: String?, template: FileTemplate, dir: PsiDirectory): PsiFile {\n        PestSettings.getInstance(dir.project).preferredTestFlavor = if (template.name == PEST_IT_TEMPLATE) TestFlavor.IT\n            else TestFlavor.TEST\n\n        var testName = name\n        if (!name!!.endsWith(\"test\", true)) {\n            testName = \"${name}Test\"\n        }\n\n        return super.createFileFromTemplate(testName, template, dir)\n    }\n\n    override fun getDefaultTemplateName(dir: PsiDirectory): String {\n        return if (PestSettings.getInstance(dir.project).preferredTestFlavor == TestFlavor.IT) {\n            PEST_IT_TEMPLATE\n        } else {\n            PEST_TEST_TEMPLATE\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/templates/PestDescribePostfixTemplate.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.intellij.openapi.editor.Document\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.postfixCompletion.PhpPostfixUtils\nimport com.jetbrains.php.postfixCompletion.PhpStringBasedPostfixTemplate\nimport com.pestphp.pest.isPestTestFile\n\nclass PestDescribePostfixTemplate : PhpStringBasedPostfixTemplate(\n    \"describe\",\n    \"describe($EXPR, function...)\",\n    PhpPostfixUtils.selectorTopmost()\n) {\n    override fun isApplicable(context: PsiElement, copyDocument: Document, newOffset: Int): Boolean {\n        return context.parent is StringLiteralExpression && context.containingFile.isPestTestFile()\n    }\n\n    override fun getTemplateString(element: PsiElement): String {\n        val dollar = \"$\"\n\n        return \"\"\"\n            describe(${dollar}$EXPR${dollar}, function() {\n                ${dollar}END${dollar}\n            });\n        \"\"\".trimIndent()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/templates/PestItPostfixTemplate.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.intellij.openapi.editor.Document\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.jetbrains.php.postfixCompletion.PhpPostfixUtils\nimport com.jetbrains.php.postfixCompletion.PhpStringBasedPostfixTemplate\nimport com.pestphp.pest.isPestTestFile\n\n/**\n * Adds a postfix template for `it` tests.\n */\nclass PestItPostfixTemplate : PhpStringBasedPostfixTemplate(\n    \"it\",\n    \"it(${EXPR}, function...)\",\n    PhpPostfixUtils.selectorTopmost()\n) {\n    override fun isApplicable(context: PsiElement, copyDocument: Document, newOffset: Int): Boolean {\n        return context.parent is StringLiteralExpression && context.containingFile.isPestTestFile()\n    }\n\n    override fun getTemplateString(element: PsiElement): String {\n        val dollar = \"$\";\n\n        return \"\"\"\n            it(${dollar}${EXPR}${dollar}, function() {\n                ${dollar}END${dollar}\n            });\n        \"\"\".trimIndent()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/templates/PestPostfixTemplateProvider.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.intellij.codeInsight.template.postfix.templates.PostfixTemplate\nimport com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.psi.PsiFile\n\n/**\n * Register postfix templates\n */\nclass PestPostfixTemplateProvider : PostfixTemplateProvider {\n    override fun getTemplates(): MutableSet<PostfixTemplate> {\n        return mutableSetOf(PestItPostfixTemplate(), PestDescribePostfixTemplate())\n    }\n\n    override fun isTerminalSymbol(currentChar: Char): Boolean {\n        return currentChar == '.'\n    }\n\n    override fun preExpand(file: PsiFile, editor: Editor) = Unit\n\n    override fun afterExpand(file: PsiFile, editor: Editor) = Unit\n\n    override fun preCheck(copyFile: PsiFile, realEditor: Editor, currentOffset: Int): PsiFile {\n        return copyFile\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/templates/PestRootTemplateContextType.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.intellij.codeInsight.template.TemplateActionContext\nimport com.intellij.codeInsight.template.TemplateContextType\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.elements.PhpNamespace\nimport com.jetbrains.php.lang.psi.elements.StringLiteralExpression\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.isPestTestFile\n\n/**\n * Adds a template context to be used in live templates\n *\n * This Pest root template checks if the context is the root of a\n * pest test file.\n */\nclass PestRootTemplateContextType : TemplateContextType(PestBundle.message(\"LIVE_TEMPLATE_PEST_ROOT\")) {\n    override fun isInContext(templateActionContext: TemplateActionContext): Boolean {\n        if (!templateActionContext.file.isPestTestFile()) {\n            return false\n        }\n\n        val element = templateActionContext.file.findElementAt(templateActionContext.startOffset)\n\n        if (element?.parent is StringLiteralExpression) {\n            return false\n        }\n\n        // Get root element\n        val root = element?.parent?.parent?.parent?.parent\n\n        // Check if in root is namespace or file\n        return root is PhpFile || root is PhpNamespace\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/types/HigherOrderExtendTypeProvider.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.MemberReference\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.jetbrains.php.lang.psi.elements.PhpNamedElement\nimport com.jetbrains.php.lang.psi.elements.PhpTypedElement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4\nimport com.pestphp.pest.features.customExpectations.expectationType\n\n\nclass HigherOrderExtendTypeProvider : PhpTypeProvider4 {\n    override fun getKey(): Char {\n        return '\\u0224' // Ȥ\n    }\n\n    override fun getType(psiElement: PsiElement): PhpType? {\n        if (DumbService.isDumb(psiElement.project)) return null\n\n        val reference = psiElement as? MemberReference ?: return null\n\n        if (reference !is FieldReference && reference !is MethodReference) return null\n\n        val expectCall = getExpectCall(reference) ?: return null\n\n        if (expectCall.parameters.isEmpty()) return null\n\n        val firstParameterType = (expectCall.parameters[0] as? PhpTypedElement)?.type ?: return null\n\n        return PhpType().add(firstParameterType).add(expectationType)\n    }\n\n    private fun getExpectCall(reference: MemberReference, depth: Int = 50): FunctionReferenceImpl? {\n        if (depth <= 0) return null\n\n        return when (val classReference = reference.classReference) {\n            is FunctionReferenceImpl -> if (classReference.name == \"expect\") classReference else null\n            is MemberReference -> getExpectCall(classReference, depth - 1)\n            else -> null\n        }\n    }\n\n    override fun complete(s: String, project: Project): PhpType? {\n        return null\n    }\n\n    override fun getBySignature(s: String, set: Set<String>, i: Int, project: Project): Collection<PhpNamedElement?> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/types/InnerTestTypeProvider.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.isAnyPestFunction\nimport com.pestphp.pest.isTestAsThisVariableInPest\n\n/**\n * Extend `test()` type inside a test closure with types from `uses`.\n * Both `uses` from the same file, the pest config file\n * and `uses` with paths from pest config file.\n */\nclass InnerTestTypeProvider: ThisTypeProvider() {\n  override fun getKey(): Char {\n    return '\\u0226' // Ȧ\n  }\n\n  override fun getType(psiElement: PsiElement): PhpType? {\n      if (DumbService.isDumb(psiElement.project)) return null\n\n      if (!psiElement.isTestAsThisVariableInPest { it.isAnyPestFunction() }) return null\n\n      return getPestType(psiElement)\n  }\n}"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/types/ThisExtendTypeProvider.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.PhpNamedElement\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4\nimport com.pestphp.pest.features.customExpectations.expectationType\nimport com.pestphp.pest.features.customExpectations.isThisVariableInExtend\n\n/**\n * Adds autocompletion for `$this` variable in `expect->extend`.\n */\nclass ThisExtendTypeProvider : PhpTypeProvider4 {\n    override fun getKey(): Char {\n        return '\\u0223' // ȣ\n    }\n\n    override fun getType(psiElement: PsiElement): PhpType? {\n        if (DumbService.isDumb(psiElement.project)) return null\n\n        if (!psiElement.isThisVariableInExtend()) return null\n\n        return expectationType\n    }\n\n    override fun complete(s: String, project: Project): PhpType? {\n        return null\n    }\n\n    override fun getBySignature(s: String, set: Set<String>, i: Int, project: Project): Collection<PhpNamedElement?> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/types/ThisFieldTypeProvider.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.PhpNamedElement\nimport com.jetbrains.php.lang.psi.elements.PhpTypedElement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4\nimport com.pestphp.pest.getAllBeforeThisAssignments\nimport com.pestphp.pest.isPestAfterFunction\nimport com.pestphp.pest.isPestTestFunction\nimport com.pestphp.pest.isThisVariableInPest\n\n/**\n * Adds type for fields registered in `beforeEach`.\n */\nclass ThisFieldTypeProvider : PhpTypeProvider4 {\n    override fun getKey(): Char {\n        return '\\u0225' // ȥ\n    }\n\n    override fun getType(psiElement: PsiElement): PhpType? {\n        if (DumbService.isDumb(psiElement.project)) return null\n\n        val fieldReference = psiElement as? FieldReference ?: return null\n\n        if (!fieldReference.classReference.isThisVariableInPest { check(it) }) return null\n\n        val fieldName = fieldReference.name ?: return null\n\n        return (psiElement.containingFile ?: return null).getAllBeforeThisAssignments()\n            .filter { (it.variable as? FieldReference)?.name == fieldName }\n            .mapNotNull { it.value }\n            .filterIsInstance<PhpTypedElement>()\n            .firstOrNull()?.type\n    }\n\n    private fun check(it: FunctionReferenceImpl) = it.isPestTestFunction() || it.isPestAfterFunction()\n\n    override fun complete(s: String, project: Project): PhpType? {\n        return null\n    }\n\n    override fun getBySignature(s: String, set: Set<String>, i: Int, project: Project): Collection<PhpNamedElement?> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/pestphp/pest/types/ThisTypeProvider.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.io.FileUtil\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.psi.PsiElement\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.PhpNamedElement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4\nimport com.pestphp.pest.PestSettings\nimport com.pestphp.pest.getPestConfigurationPhpType\nimport com.pestphp.pest.getRoot\nimport com.pestphp.pest.isAnyPestFunction\nimport com.pestphp.pest.isThisVariableInPest\nimport java.nio.file.FileSystems\nimport kotlin.io.path.Path\n\n/**\n * Extend `$this` type with types from `uses`.\n * Both `uses` from the same file, the pest config file\n * and `uses` with paths from pest config file.\n */\nopen class ThisTypeProvider : PhpTypeProvider4 {\n    override fun getKey(): Char {\n        return '\\u0221' // ȡ\n    }\n\n    override fun getType(psiElement: PsiElement): PhpType? {\n        if (DumbService.isDumb(psiElement.project)) return null\n\n        if (\n            ((psiElement as? FunctionReferenceImpl)?.isAnyPestFunction() != true) &&\n            !psiElement.isThisVariableInPest { it.isAnyPestFunction() }\n        ) return null\n\n        return getPestType(psiElement)\n    }\n\n    protected fun getPestType(psiElement: PsiElement): PhpType? {\n        val virtualFile = psiElement.containingFile?.originalFile?.virtualFile ?: return null\n\n        val config = PestSettings.getInstance(psiElement.project).getPestConfiguration(psiElement.project, virtualFile)\n\n        val baseDir = (psiElement.project.guessProjectDir() ?: return config.baseTestType)\n        val relativePath = VfsUtil.getRelativePath(virtualFile, baseDir) ?: return config.baseTestType\n\n        val result = PhpType().add(config.baseTestType)\n        val defaultFileSystem = FileSystems.getDefault()\n\n        config.pathsClasses.forEach { (path, type) ->\n            FileUtil.toCanonicalPath(path)?.let { normalizedPathForMatching ->\n                if (defaultFileSystem.getPathMatcher(\"glob:$normalizedPathForMatching**\").matches(Path(relativePath))) {\n                    result.add(type)\n                }\n            }\n        }\n\n        psiElement.containingFile.getRoot()\n            .filterIsInstance<FunctionReference>()\n            .mapNotNull { it.getPestConfigurationPhpType() }\n            .forEach { result.add(it) }\n\n        return result\n    }\n\n    override fun complete(s: String, project: Project): PhpType? {\n        return null\n    }\n\n    override fun getBySignature(s: String, set: Set<String>, i: Int, project: Project): Collection<PhpNamedElement?> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "src/main/resources/META-INF/plugin.xml",
    "content": "<idea-plugin allow-bundled-update=\"true\">\n    <id>com.pestphp.pest-intellij</id>\n    <name>Pest</name>\n    <vendor>JetBrains</vendor>\n    <description>Plugin provides Pest PHP test framework support</description>\n    <resource-bundle>messages.pestBundle</resource-bundle>\n    <category>Test Tools</category>\n\n    <dependencies>\n      <!-- region Generated dependencies - run `Generate Product Layouts` to regenerate -->\n      <plugin id=\"com.jetbrains.php\"/>\n      <module name=\"intellij.xml.dom.impl\"/>\n      <module name=\"intellij.xml.psi.impl\"/>\n      <!-- endregion -->\n    </dependencies>\n    <content namespace=\"jetbrains\">\n        <module name=\"intellij.pest.coverage\"/>\n    </content>\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <console.folding implementation=\"com.pestphp.pest.runner.PestPromptConsoleFolding\"/>\n        <psi.declarationProvider implementation=\"com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationSymbolDeclarationProvider\"/>\n        <psi.symbolReferenceProvider hostElementClass=\"com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl\"\n                                     hostLanguage=\"PHP\"\n                                     implementationClass=\"com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationReferenceProvider\"\n                                     targetClass=\"com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationSymbol\"/>\n        <searcher forClass=\"com.intellij.find.usages.api.UsageSearchParameters\"\n                  implementationClass=\"com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationUsageSearcher\"/>\n        <searcher forClass=\"com.intellij.refactoring.rename.api.RenameUsageSearchParameters\"\n                  implementationClass=\"com.pestphp.pest.features.customExpectations.symbols.PestCustomExpectationRenameUsageSearcher\"/>\n\n        <stacktrace.fold substring=\"at vendor/\"/>\n        <codeInsight.parameterInfo language=\"PHP\" order=\"before phpParameterInfo\"\n                                   implementationClass=\"com.pestphp.pest.features.customExpectations.CustomExpectationParameterInfoHandler\"/>\n        <runLineMarkerContributor\n                language=\"PHP\"\n                implementationClass=\"com.pestphp.pest.PestTestRunLineMarkerProvider\"\n        />\n        <codeInsight.lineMarkerProvider\n                language=\"PHP\"\n                implementationClass=\"com.pestphp.pest.features.snapshotTesting.SnapshotLineMarkerProvider\"\n        />\n\n\n        <programRunner implementation=\"com.pestphp.pest.configuration.PestDebugRunner\"/>\n        <configurationType implementation=\"com.pestphp.pest.configuration.PestRunConfigurationType\"/>\n        <runConfigurationProducer implementation=\"com.pestphp.pest.configuration.PestRunConfigurationProducer\"/>\n        <fileIconProvider implementation=\"com.pestphp.pest.PestIconProvider\" order=\"before PhpFileIconProvider\"/>\n        <postStartupActivity implementation=\"com.pestphp.pest.features.customExpectations.CustomExpectationRemoveGeneratedFileStartupActivity\"/>\n        <testFinder implementation=\"com.pestphp.pest.goto.PestTestFinder\"/>\n        <gotoTargetPresentationProvider implementation=\"com.pestphp.pest.goto.PestGotoTargetPresentationProvider\"/>\n        <fileBasedIndex implementation=\"com.pestphp.pest.indexers.PestTestIndex\"/>\n        <fileBasedIndex implementation=\"com.pestphp.pest.features.customExpectations.CustomExpectationIndex\"/>\n        <annotator language=\"PHP\" implementationClass=\"com.pestphp.pest.annotator.PestAnnotator\"/>\n\n        <localInspection\n                language=\"PHP\"\n                groupPath=\"PHP\"\n                groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n                shortName=\"InvalidTestNameCaseInspection\"\n                enabledByDefault=\"true\"\n                key=\"INSPECTION_INVALID_TEST_NAME_CASE\"\n                level=\"WEAK WARNING\"\n                implementationClass=\"com.pestphp.pest.inspections.InvalidTestNameCaseInspection\"\n        />\n        <localInspection\n                language=\"PHP\"\n                groupPath=\"PHP\"\n                groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n                shortName=\"MultipleExpectChainableInspection\"\n                enabledByDefault=\"true\"\n                key=\"INSPECTION_MULTIPLE_CHAINABLE_EXPECT_CALLS\"\n                level=\"WEAK WARNING\"\n                implementationClass=\"com.pestphp.pest.inspections.MultipleExpectChainableInspection\"\n        />\n\n        <completion.contributor implementationClass=\"com.pestphp.pest.completion.PestCompletionContributor\"\n                                language=\"PHP\"/>\n        <gotoDeclarationHandler implementation=\"com.pestphp.pest.completion.ThisFieldsCompletionProvider\"/>\n        <gotoDeclarationHandler implementation=\"com.pestphp.pest.goto.PestDatasetUsagesGotoHandler\"/>\n        <lang.inspectionSuppressor\n                language=\"PHP\"\n                implementationClass=\"com.pestphp.pest.inspections.SuppressExpressionResultUnusedInspection\"/>\n        <lang.inspectionSuppressor\n                language=\"PHP\"\n                implementationClass=\"com.pestphp.pest.inspections.SuppressUndefinedPropertyInspection\"\n        />\n        <notificationGroup displayType=\"BALLOON\" id=\"Outdated Pest\" isLogByDefault=\"true\" bundle=\"messages.pestBundle\"\n                           key=\"NOTIFICATION_GROUP_OUTDATED_PEST\"/>\n        <lang.structureViewExtension implementation=\"com.pestphp.pest.structureView.PestStructureViewExtension\"/>\n        <gotoSymbolContributor implementation=\"com.pestphp.pest.goto.PestTestGoToSymbolContributor\"/>\n        <liveTemplateContext\n                implementation=\"com.pestphp.pest.templates.PestRootTemplateContextType\"\n                contextId=\"ROOT_PESTPHP\"/>\n        <defaultLiveTemplates file=\"/liveTemplates/PestPHP.xml\"/>\n        <codeInsight.template.postfixTemplateProvider\n                language=\"PHP\"\n                implementationClass=\"com.pestphp.pest.templates.PestPostfixTemplateProvider\"/>\n        <lang.surroundDescriptor\n                language=\"PHP\"\n                implementationClass=\"com.pestphp.pest.surrounders.StatementSurroundDescriptor\"/>\n        <fileBasedIndex implementation=\"com.pestphp.pest.features.datasets.DatasetIndex\"/>\n        <localInspection\n                language=\"PHP\"\n                groupPath=\"PHP\"\n                groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n                shortName=\"InvalidDatasetReferenceInspection\"\n                enabledByDefault=\"true\"\n                key=\"INSPECTION_INVALID_DATASET_REFERENCE\"\n                level=\"ERROR\"\n                implementationClass=\"com.pestphp.pest.features.datasets.InvalidDatasetReferenceInspection\"\n        />\n        <localInspection\n                language=\"PHP\"\n                groupPath=\"PHP\"\n                groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n                shortName=\"InvalidDatasetNameCaseInspection\"\n                enabledByDefault=\"true\"\n                key=\"INSPECTION_INVALID_DATASET_NAME_CASE\"\n                level=\"WEAK WARNING\"\n                implementationClass=\"com.pestphp.pest.features.datasets.InvalidDatasetNameCaseInspection\"\n        />\n        <psi.referenceContributor\n                implementation=\"com.pestphp.pest.features.datasets.DatasetReferenceContributor\"\n                language=\"PHP\"/>\n        <psi.referenceContributor\n                implementation=\"com.pestphp.pest.features.configuration.ConfigurationReferenceContributor\"\n                language=\"PHP\"/>\n        <registryKey defaultValue=\"true\"\n                     description=\"Whether custom expectation file generation should happen.\"\n                     key=\"pestphp.custom-expectations\"\n        />\n        <internalFileTemplate name=\"Pest It\"/>\n        <internalFileTemplate name=\"Pest Test\"/>\n        <internalFileTemplate name=\"Pest Scoped Dataset\"/>\n        <internalFileTemplate name=\"Pest Shared Dataset\"/>\n        <localInspection\n          language=\"PHP\"\n          groupPath=\"PHP\"\n          groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n          shortName=\"PestAssertionCanBeSimplifiedInspection\"\n          enabledByDefault=\"true\"\n          key=\"INSPECTION_ASSERTION_CAN_BE_SIMPLIFIED_NAME\"\n          level=\"WEAK WARNING\"\n          implementationClass=\"com.pestphp.pest.inspections.PestAssertionCanBeSimplifiedInspection\"\n        />\n        <localInspection\n          language=\"PHP\"\n          groupPath=\"PHP\"\n          groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n          shortName=\"PestTestFailedLineInspection\"\n          enabledByDefault=\"true\"\n          key=\"INSPECTION_PEST_FAILED_LINE\"\n          level=\"WARNING\"\n          implementationClass=\"com.pestphp.pest.inspections.PestTestFailedLineInspection\"\n        />\n        <localInspection\n          language=\"PHP\"\n          groupPath=\"PHP\"\n          groupKey=\"INSPECTION_PHP_GROUP_PEST\"\n          shortName=\"MissingScreenshotSnapshotInspection\"\n          enabledByDefault=\"true\"\n          key=\"INSPECTION_MISSING_SCREENSHOT_SNAPSHOT_NAME\"\n          level=\"WARNING\"\n          implementationClass=\"com.pestphp.pest.inspections.MissingScreenshotSnapshotInspection\"\n        />\n        <statistics.counterUsagesCollector implementationClass=\"com.pestphp.pest.statistics.PestUsagesCollector\"/>\n        <programRunner implementation=\"com.pestphp.pest.features.parallel.PestParallelProgramRunner\"/>\n        <executor implementation=\"com.pestphp.pest.features.parallel.PestParallelTestExecutor\" order=\"first,after debug\"/>\n        <projectSettings service=\"com.pestphp.pest.PestSettings\"/>\n    </extensions>\n\n    <extensions defaultExtensionNs=\"com.jetbrains.php\">\n        <testFrameworkType implementation=\"com.pestphp.pest.PestFrameworkType\"/>\n        <composerConfigClient implementation=\"com.pestphp.pest.PestComposerConfig\"/>\n\n        <typeProvider4 implementation=\"com.pestphp.pest.types.ThisTypeProvider\"/>\n        <typeProvider4 implementation=\"com.pestphp.pest.types.ThisFieldTypeProvider\"/>\n        <typeProvider4 implementation=\"com.pestphp.pest.types.ThisExtendTypeProvider\"/>\n        <typeProvider4 implementation=\"com.pestphp.pest.types.HigherOrderExtendTypeProvider\"/>\n        <typeProvider4 implementation=\"com.pestphp.pest.types.InnerTestTypeProvider\"/>\n    </extensions>\n\n    <actions>\n        <action id=\"PestPressToContinue\"\n                class=\"com.pestphp.pest.runner.PestPressToContinueAction\"\n                icon=\"/run.svg\">\n            <add-to-group group-id=\"RunTab.TopToolbar\" anchor=\"after\" relative-to-action=\"Rerun\"/>\n            <add-to-group group-id=\"RunTab.TopToolbar.Old\" anchor=\"after\" relative-to-action=\"Rerun\"/>\n        </action>\n        <action id=\"Pest.New.File\"\n                class=\"com.pestphp.pest.templates.PestConfigNewFileAction\"\n                icon=\"/file.svg\">\n            <add-to-group group-id=\"PhpNewTestGroup\" anchor=\"before\" relative-to-action=\"Pest.New.Dataset\"/>\n        </action>\n        <action id=\"Pest.New.Dataset\"\n                class=\"com.pestphp.pest.templates.PestConfigNewDatasetFileAction\"\n                icon=\"/dataset.svg\">\n            <add-to-group group-id=\"PhpNewTestGroup\" anchor=\"last\"/>\n        </action>\n        <action id=\"Pest.New.File.From.Class\" class=\"com.pestphp.pest.PestNewTestFromClassAction\">\n        </action>\n    </actions>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/fileTemplates/internal/Pest It.php.ft",
    "content": "<?php\n#parse(\"PHP File Header.php\")\n\nit('#[[$END$]]#', function () {\n\n});\n"
  },
  {
    "path": "src/main/resources/fileTemplates/internal/Pest Scoped Dataset.php.ft",
    "content": "<?php\n#parse(\"PHP File Header.php\")\n\ndataset('#[[$DATASET_NAME$]]#', [\n    '#[[$END$]]#'\n]);\n"
  },
  {
    "path": "src/main/resources/fileTemplates/internal/Pest Shared Dataset.php.ft",
    "content": "<?php\n#parse(\"PHP File Header.php\")\n\ndataset('#[[$DATASET_NAME$]]#', [\n    '#[[$END$]]#'\n]);\n"
  },
  {
    "path": "src/main/resources/fileTemplates/internal/Pest Test.php.ft",
    "content": "<?php\n#parse(\"PHP File Header.php\")\n\ntest('#[[$END$]]#', function () {\n\n});\n"
  },
  {
    "path": "src/main/resources/fileTemplates/internal/Pest file from class.php.ft",
    "content": "<?php\n#parse(\"PHP File Header.php\")\n\n#if (${NAMESPACE})\nnamespace ${NAMESPACE};\n#end\n\ntest('#[[$END$]]#', function () {\n\n});"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/InvalidDatasetNameCaseInspection.html",
    "content": "<html lang=\"en\">\n<body>\nReports invalid names for pest dataset cases.\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/InvalidDatasetReferenceInspection.html",
    "content": "<html lang=\"en\">\n<body>\nReports a non-existent Pest dataset.\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/InvalidTestNameCaseInspection.html",
    "content": "<html lang=\"en\">\n<body>\nReports invalid names for pest test cases.\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/MissingScreenshotSnapshotInspection.html",
    "content": "<html lang=\"en\">\n<body>\nReports missing visual regression snapshot files for <code>$var->assertScreenshotMatches()</code> calls in Pest tests.\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/MultipleExpectChainableInspection.html",
    "content": "<html lang=\"en\">\n<body>\nReports multiple expectations that can be converted to chainable calls.\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/PestAssertionCanBeSimplifiedInspection.html",
    "content": "<html lang=\"en\">\n<body>\nReports assertion call to be replaced with more specific analogue\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/inspectionDescriptions/PestTestFailedLineInspection.html",
    "content": "<html>\n<body>\n<p>Reports failed function calls or assertions in Pest tests. It helps detect the failed line in code faster and start debugging it immediately.</p>\n<p><b>Example:</b></p>\n<pre><code>\n  test(\"1 is 2\", function() {\n      expect(1)->toBe(2); //highlighted\n  }\n</code></pre>\n<!-- tooltip end -->\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/liveTemplates/PestPHP.xml",
    "content": "<templateSet group=\"PestPHP\">\n    <template name=\"it\" value=\"it('$NAME$', function() {&#10;    $END$&#10;});\" description=\"Create a pest it test\"\n              toReformat=\"true\" toShortenFQNames=\"true\">\n        <variable name=\"NAME\" defaultValue=\"\" alwaysStopAt=\"true\"/>\n        <context>\n            <option name=\"ROOT_PESTPHP\" value=\"true\"/>\n        </context>\n    </template>\n    <template name=\"test\" value=\"test('$NAME$', function() {&#10;    $END$&#10;});\" description=\"Create a pest it test\"\n              toReformat=\"true\" toShortenFQNames=\"true\">\n        <variable name=\"NAME\" defaultValue=\"\" alwaysStopAt=\"true\"/>\n        <context>\n            <option name=\"ROOT_PESTPHP\" value=\"true\"/>\n        </context>\n    </template>\n</templateSet>"
  },
  {
    "path": "src/main/resources/log4j.properties",
    "content": "log4j.rootLogger=INFO,stdout\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\n"
  },
  {
    "path": "src/main/resources/messages/pestBundle.properties",
    "content": "CREATE_NEW_PEST_TEST=Create New Pest Test\\u2026\nCREATE_NEW_PEST_TEST_DESCRIPTION=Creates a new Pest test based on a file\nFRAMEWORK_NAME=Pest\nNO_FAILED_TESTS_FOUND=Failed finding any tests to rerun <br>Rerunning support was first added in Pest v0.3.5\n\nPEST_CONFIGURATION_UI_CAN_NOT_PARSE_VERSION=Cannot parse version command output.\\n{0}\nGETTING_PEST_VERSION=Getting Pest version\\u2026\n\nCREATE_NEW_PEST_IT_FLAVOR=Test file - it(...)\nCREATE_NEW_PEST_TEST_FLAVOR=Test file - test(...)\nCREATE_NEW_PEST_TEST_DIALOG_TITLE=Create Pest Test File\nCREATE_NEW_PEST_DATASET_DIALOG_TITLE=Create Pest Dataset File\nCREATE_NEW_PEST_SHARED_DATASET=Shared dataset\nCREATE_NEW_PEST_SCOPED_DATASET=Scoped dataset\n\nLIVE_TEMPLATE_PEST_ROOT=Pest root\nEXPECT_STATEMENT=Expect\nRUNTIME_CONFIGURATION_EXCEPTION_MESSAGE=''{0}'' for ''{1}'' run configuration\nCANNOT_RUN_PEST_WITH_TYPE_MESSAGE=Cannot run Pest with type\nCOVERAGE_ENGINE_LABEL_TEXT=Preferred Coverage engine:\nENABLE_PARALLEL_TESTING_LABEL_TEXT=Enable parallel testing:\nNOTIFICATION_GROUP_OUTDATED_PEST=Outdated Pest\nDIALOG_MESSAGE_COULD_NOT_FIND_PHP_INTERPRETER=Could not find PHP interpreter\nTOOLTIP_NAVIGATE_TO_SNAPSHOT_FILES=Navigate to snapshot files\n\naction.Pest.New.File.text=Pest Test\naction.Pest.New.Dataset.text=Pest Dataset\nACTIONS_NEW_PEST_TEST_ACTION_DESCRIPTION=Creates new Pest test\n\nINSPECTION_PHP_GROUP_PEST=Pest\nINSPECTION_MULTIPLE_CHAINABLE_EXPECT_CALLS=Multiple expectations can be chained together\nINSPECTION_INVALID_TEST_NAME_CASE=Words in Pest test names should be separated by spaces\nINSPECTION_INVALID_DATASET_NAME_CASE=Words in Pest dataset names should be separated by spaces\nINSPECTION_DUPLICATE_TEST_NAME=Pest test names must be unique within a file\nINSPECTION_DUPLICATE_CUSTOM_EXPECTATION=Pest custom expectation names must be unique\nINSPECTION_INVALID_DATASET_REFERENCE=The dataset does not exist\nINSPECTION_ASSERTION_CAN_BE_SIMPLIFIED_NAME=Assertion can be simplified\nINSPECTION_ASSERTION_CAN_BE_SIMPLIFIED=Assertion ''{0}'' can be simplified to ''{1}''\nINSPECTION_PEST_FAILED_LINE=Failed line in test\nINSPECTION_MISSING_SCREENSHOT_SNAPSHOT_NAME=Missing screenshot snapshot\nINSPECTION_MISSING_SCREENSHOT_SNAPSHOT=Missing screenshot snapshot, run the test to create it\n\nQUICK_FIX_CHANGE_TEST_NAME_CASING=Convert test name to sentence case\nQUICK_FIX_CHANGE_DATASET_NAME_CASING=Convert dataset name to sentence case\nQUICK_FIX_CHANGE_MULTIPLE_EXPECT_INTO_CHAIN=Convert multiple expectations into chain\nQUICK_FIX_SIMPLIFY_ASSERTION=Simplify assertion\nQUICK_FIX_DELETE_TEST=Delete test ''{0}''\nQUICK_FIX_DELETE_CUSTOM_EXPECTATION=Delete expectation ''{0}''\n\nINTENTION_NAVIGATE_TO_DUPLICATE_TEST_NAME=Navigate to duplicate test name\nINTENTION_NAVIGATE_TO_DUPLICATE_CUSTOM_EXPECTATION=Navigate to duplicate custom expectation\nACTION_RUN_SELECTED_CONFIGURATION_WITH_MUTATE_DESCRIPTION=Run selected configuration with --mutate\nACTION_PEST_MUTATE_TEXT=Pest Mutation\nRUN_PEST_WITH_MUTATE=Run Pest with Mutation\nRUN_S_WITH_MUTATE=Run%s with Mutation\nACTION_RUN_SELECTED_CONFIGURATION_WITH_PARALLEL_DESCRIPTION=Run selected configuration with --parallel\nACTION_PEST_PARALLEL_TEXT=Pest Parallel\nRUN_PEST_WITH_PARALLEL=Run Pest in Parallel\nRUN_S_WITH_PARALLEL=Run%s in Parallel\nPEST_PARALLEL_IS_NOT_SUPPORTED_FOR_SELECTED_RUN_PROFILE=Pest --parallel is not compatible with selected run profile\nPEST_VERSION_IS_NOT_SUPPORTED_FOR_REMOTE_INTERPRETER=Pest version is not supported for remote interpreter\nMUTATION_TESTING_IS_AVAILABLE_FROM_VERSION_3=Mutation testing is available in Pest starting from version 3.0\nPARALLEL_TESTING_IS_SUPPORTED_FROM_VERSION_2=Parallel testing is supported in PhpStorm starting from Pest version 2.0\nCURRENT_PEST_VERSION_IS=Current Pest version is {0}\n\naction.press.to.continue.text=Continue Test Run"
  },
  {
    "path": "src/main/resources/postfixTemplates/PestDescribePostfixTemplate/after.php.template",
    "content": "<?php\ndescribe(\"can describe this\", function() {\n};"
  },
  {
    "path": "src/main/resources/postfixTemplates/PestDescribePostfixTemplate/before.php.template",
    "content": "<?php\n<spot>\"can describe this\"</spot>$key"
  },
  {
    "path": "src/main/resources/postfixTemplates/PestDescribePostfixTemplate/description.html",
    "content": "<html lang=\"en\">\n<body>\n<p>\n    Wraps a string into a pest describe block.\n</p>\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/postfixTemplates/PestItPostfixTemplate/after.php.template",
    "content": "<?php\nit(\"can test this\", function() {\n};"
  },
  {
    "path": "src/main/resources/postfixTemplates/PestItPostfixTemplate/before.php.template",
    "content": "<?php\n<spot>\"can test this\"</spot>$key"
  },
  {
    "path": "src/main/resources/postfixTemplates/PestItPostfixTemplate/description.html",
    "content": "<html lang=\"en\">\n<body>\n<p>\n    Wraps a string into a pest it test.\n</p>\n</body>\n</html>"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/PestIconProviderTest.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.util.Iconable.ICON_FLAG_VISIBILITY\nimport com.intellij.psi.PsiManager\n\nclass PestIconProviderTest : PestLightCodeFixture() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyFileToProject(\"utilTests/SimpleTest.php\", \"tests/SimpleTest.php\")\n    }\n\n    fun testCanGetPestIconForPestFile() {\n        val file = myFixture.configureByFile(\"tests/SimpleTest.php\")\n\n        assertEquals(\n            PestIcons.File,\n            PestIconProvider().getIcon(file.virtualFile, ICON_FLAG_VISIBILITY, project),\n        )\n    }\n\n    fun testCanGetOtherIconForNonPestFile() {\n        val file = myFixture.configureByFile(\"SimpleScript.php\")\n\n        assertNull(\n            PestIconProvider().getIcon(file.virtualFile, ICON_FLAG_VISIBILITY, project)\n        )\n    }\n\n    fun testCanGetPestIconForDatasetFile() {\n        myFixture.copyFileToProject(\"Dataset.php\", \"/tests/Datasets/Dataset.php\")\n        val file = myFixture.configureByFile(\"tests/Datasets/Dataset.php\")\n\n        assertEquals(\n            PestIcons.Dataset,\n            PestIconProvider().getIcon(file.virtualFile, ICON_FLAG_VISIBILITY, project)\n        )\n    }\n\n    fun testCanGetPestIconForDatasetFileWithTests() {\n        myFixture.copyFileToProject(\"TestWithDataset.php\", \"/tests/Datasets/TestWithDataset.php\")\n        val file = myFixture.configureByFile(\"tests/Datasets/TestWithDataset.php\")\n\n        assertEquals(\n            PestIcons.File,\n            PestIconProvider().getIcon(file.virtualFile, ICON_FLAG_VISIBILITY, project)\n        )\n    }\n\n    fun testCanGetPestIconForPestBaseFile() {\n        val virtualFile = myFixture.copyFileToProject(\"Pest.php\", \"tests/Pest.php\")\n\n        val file = PsiManager.getInstance(project).findFile(virtualFile)!!\n\n        assertEquals(\n            PestIcons.Logo,\n            PestIconProvider().getIcon(file.virtualFile, ICON_FLAG_VISIBILITY, project)\n        )\n    }\n\n    fun testCanGetPestIconForPestFileWithPropertyCall() {\n        val virtualFile = myFixture.copyFileToProject(\"SimpleHigherOrderNotTest.php\", \"tests/SimpleHigherOrderNotTest.php\")\n\n        val file = PsiManager.getInstance(project).findFile(virtualFile)!!\n\n        assertEquals(\n            PestIcons.File,\n            PestIconProvider().getIcon(file.virtualFile, ICON_FLAG_VISIBILITY, project)\n        )\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/PestLightCodeFixture.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.openapi.util.io.FileUtil\nimport com.intellij.psi.PsiFile\nimport com.intellij.testFramework.TestDataPath\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.lang.PhpFileType\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport io.mockk.clearAllMocks\nimport io.mockk.unmockkAll\nimport java.io.File\nimport java.nio.file.Files\nimport kotlin.io.path.Path\n\n@Suppress(\"UnnecessaryAbstractClass\")\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest\")\nabstract class PestLightCodeFixture : BasePlatformTestCase() {\n    companion object {\n        private const val TEST_DATA_REL_PATH = \"src/test/resources/com/pestphp/pest\"\n        private const val MONOREPO_PREFIX = \"phpstorm/pest\"\n\n        private val baseTestDataPath: String by lazy {\n            val homePath = IdeaTestExecutionPolicy.getHomePathWithPolicy()\n            if (Files.exists(Path(homePath, MONOREPO_PREFIX, TEST_DATA_REL_PATH))) {\n                \"/$MONOREPO_PREFIX/$TEST_DATA_REL_PATH\"\n            }\n            else {\n                \"/$TEST_DATA_REL_PATH\"\n            }\n        }\n    }\n\n    override fun getBasePath() = baseTestDataPath\n\n    override fun setUp() {\n        super.setUp()\n        val pestStubsFile = File(\"${IdeaTestExecutionPolicy.getHomePathWithPolicy()}$baseTestDataPath/stubs.php\")\n        if (pestStubsFile.exists()) {\n            myFixture.copyFileToProject(pestStubsFile.absolutePath, \"stubs.php\")\n        }\n    }\n\n    override fun tearDown() {\n        try {\n            clearAllMocks()\n            unmockkAll()\n        }\n        catch (e: Throwable) {\n            addSuppressedException(e)\n        }\n        finally {\n            super.tearDown()\n        }\n    }\n\n    private val testNameSeparator = '#'\n\n    protected fun assertCompletion(vararg shouldContain: String) {\n        myFixture.completeBasic()\n\n        val strings = myFixture.lookupElementStrings ?: return fail(\"empty completion result\")\n\n        assertContainsElements(strings, shouldContain.asList())\n    }\n\n    protected fun assertAllCompletion(vararg shouldContain: String) {\n        myFixture.completeBasic()\n\n        val strings = myFixture.lookupElementStrings ?: return fail(\"empty completion result\")\n\n        assertEquals(shouldContain.toList(), strings)\n    }\n\n    protected fun assertNoCompletion() {\n        myFixture.completeBasic()\n\n        val strings = myFixture.lookupElementStrings\n\n        assertNullOrEmpty(strings)\n    }\n\n    protected fun createPestFrameworkConfiguration(): PhpTestFrameworkConfiguration? {\n        val configuration = PhpTestFrameworkSettingsManager\n            .getInstance(myFixture.project)\n            .getOrCreateByInterpreter(PestFrameworkType.instance, PhpInterpreter(), null, false)\n        configuration?.executablePath = \"randomPath\"\n\n        return configuration\n    }\n\n    protected fun configureByPhpCode(code: String) {\n        myFixture.configureByText(PhpFileType.INSTANCE, \"<?php $code\")\n    }\n\n    private fun configureByDirectory(relativePathToDirectory: String) {\n        val fromFile = File(\"$testDataPath/$relativePathToDirectory\")\n        if (fromFile.exists() && fromFile.isDirectory) {\n            fromFile.listFiles()?.forEach { file ->\n                val relativePath = FileUtil.toSystemIndependentName(file.path).removePrefix(\"$testDataPath/\")\n                myFixture.copyFileToProject(relativePath)\n            }\n        }\n    }\n\n    protected fun configureByFile(): PsiFile {\n        val path = getFileNameBeforeRelativePath()\n        configureByDirectory(path)\n        return myFixture.configureByFile(path)\n    }\n\n    private fun getFullPath(basePath: String, relativePath: String): String {\n        return \"$basePath/$relativePath\"\n    }\n\n    protected fun getFileNameBeforeRelativePath(): String {\n        return getTestName(false).let { testPath ->\n            val phpTestPath = \"$testPath.php\"\n            if (phpTestPath.contains(testNameSeparator)) {\n                phpTestPath.replace(testNameSeparator, File.separatorChar)\n            } else {\n                phpTestPath\n            }\n        }\n    }\n\n    protected fun getFileBeforeFullPath(): String {\n        return getFullPath(testDataPath, getFileNameBeforeRelativePath())\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/PestTestRunLineMarkerProviderTest.kt",
    "content": "package com.pestphp.pest\n\nimport com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.configuration.PestRunConfigurationType\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest\")\nclass PestTestRunLineMarkerProviderTest : PestLightCodeFixture() {\n    private lateinit var pestConfigurations: List<PhpTestFrameworkConfiguration>\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestTestRunLineMarkerProviderTest\"\n\n    override fun setUp() {\n        super.setUp()\n        pestConfigurations = PhpTestFrameworkSettingsManager.getInstance(project).getConfigurations(PestFrameworkType.instance)\n    }\n\n    override fun tearDown() {\n        try {\n            PhpTestFrameworkSettingsManager.getInstance(project).setConfigurations(PestFrameworkType.instance, pestConfigurations)\n        } catch (e: Throwable) {\n            addSuppressedException(e)\n        } finally {\n            super.tearDown()\n        }\n    }\n\n    private fun doTest(vararg expectedMarkerLines: Int) {\n        myFixture.doHighlighting()\n        val editor = myFixture.editor\n\n        val markerList = DaemonCodeAnalyzerImpl.getLineMarkers(editor.document, project)\n        val actualMarkerLines = markerList.map { marker -> editor.offsetToLogicalPosition(marker.startOffset).line }\n        assertSameElements(actualMarkerLines, expectedMarkerLines.toList())\n    }\n\n    private fun initConfiguration() {\n        val configuration = createPestFrameworkConfiguration()\n        PhpTestFrameworkSettingsManager.getInstance(project).setConfigurations(PestFrameworkType.instance, listOf(configuration))\n    }\n\n    fun testMethodCallNamedItAndVariableTestIsNotPestTest() {\n        myFixture.configureByFile(\"MethodCallNamedItAndVariableTest.php\")\n        doTest()\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndClosure() {\n        myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndClosure.php\")\n        doTest(0, 2)\n    }\n\n    fun testFunctionCallNamedItRedefinition() {\n        myFixture.configureByFile(\"PestItFunctionCallWithRedefinition.php\")\n        doTest()\n    }\n\n    fun testFunctionCallNamedTestWithoutPest() {\n        myFixture.configureByFile(\"FunctionCallNamedTestWithoutPest.php\")\n        doTest()\n    }\n\n    fun testAssignmentFunctionCallNamedTestWithoutPest() {\n        myFixture.configureByFile(\"AssignmentFunctionCallNamedTestWithoutPest.php\")\n        doTest()\n    }\n\n    fun testAssignmentFunctionCallNamedTest() {\n        myFixture.configureByFile(\"AssignmentFunctionCallNamedTest.php\")\n        doTest()\n    }\n\n    fun testFunctionCallNamedTestAsArgument() {\n        myFixture.configureByFile(\"FunctionCallNamedTestAsArgument.php\")\n        doTest()\n    }\n\n    fun testFunctionCallNamedTestInsideDescribeBlock() {\n        myFixture.configureByFile(\"FunctionCallNamedTestInsideDescribeBlock.php\")\n        doTest(0, 2, 3)\n    }\n\n    fun testFunctionCallNamedTestInsideTest() {\n        myFixture.configureByFile(\"FunctionCallNamedTestInsideTest.php\")\n        doTest(0, 2)\n    }\n\n    fun testDataSetsAreNotYetMarkedAsRunnable() {\n        myFixture.configureByFile(\"NamedDataSets.php\")\n        doTest(0, 2, 3, 9)\n    }\n\n     fun testRunContextFromTestDirectory() {\n         initConfiguration()\n         val rootDirectory = myFixture.copyDirectoryToProject(\"contextProject\", \".\")\n         val context = myFixture.psiManager.findDirectory(rootDirectory.findChild(\"tests\")!!)!!\n         val configurationsFromContext = ConfigurationContext(context).configurationsFromContext!!\n\n         assertSize(2, configurationsFromContext)\n     }\n\n    fun testRunContextFromPestTestFile() {\n        initConfiguration()\n        myFixture.copyDirectoryToProject(\"contextProject\", \".\")\n        val context = myFixture.configureByFile(\"contextProject/tests/Test.php\")\n        val configurationsFromContext = ConfigurationContext(context).configurationsFromContext!!\n\n        assertSize(1, configurationsFromContext)\n        assertEquals(PestRunConfigurationType.instance, configurationsFromContext[0].configurationType)\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/annotator/PestAnnotatorTest.kt",
    "content": "package com.pestphp.pest.annotator\n\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestAnnotatorTest: PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/annotator\"\n\n    override fun setUp() {\n        super.setUp()\n        myFixture.copyDirectoryToProject(\"stub\", \".\")\n    }\n\n    fun testHasDuplicateCustomExpectation() {\n        myFixture.configureByFile(\"DuplicateCustomExpectation.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testDeleteDuplicateCustomExpectation() {\n        myFixture.configureByFile(\"DuplicateCustomExpectation.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().first { it.familyName == PestBundle.message(\"QUICK_FIX_DELETE_CUSTOM_EXPECTATION\", \"toBeOne\") }\n            .run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"DuplicateCustomExpectation.afterDelete.php\")\n    }\n\n    fun testNavigateToNextDuplicateCustomExpectation() {\n        myFixture.configureByFile(\"DuplicateCustomExpectation.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().last { it.familyName == PestBundle.message(\"INTENTION_NAVIGATE_TO_DUPLICATE_CUSTOM_EXPECTATION\") }\n            .run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"DuplicateCustomExpectation.afterNavigate.php\")\n    }\n\n    fun testNoDuplicateCustomExpectation() {\n        myFixture.configureByFile(\"NoDuplicateCustomExpectation.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testHasDuplicateTest() {\n        myFixture.configureByFile(\"DuplicateTestName.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testDeleteDuplicateTest() {\n        myFixture.configureByFile(\"DuplicateTestName.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().first { it.familyName == PestBundle.message(\"QUICK_FIX_DELETE_TEST\", \"basic\") }\n            .run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"DuplicateTestName.afterDelete.php\")\n    }\n\n    fun testNavigateToDuplicateTest() {\n        myFixture.configureByFile(\"DuplicateTestName.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().last { it.familyName == PestBundle.message(\"INTENTION_NAVIGATE_TO_DUPLICATE_TEST_NAME\") }\n            .run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"DuplicateTestName.afterNavigate.php\")\n    }\n\n    fun testNoDuplicateTest() {\n        myFixture.configureByFile(\"NoDuplicateTestName.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testHasDuplicateTestInDescribeBlock() {\n        myFixture.configureByFile(\"DuplicateTestNameInDescribeBlock.php\")\n\n        myFixture.checkHighlighting()\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/codeInsight/typeInference/PestTypeInferenceTest.kt",
    "content": "package com.pestphp.pest.codeInsight.typeInference\n\nimport com.intellij.openapi.application.WriteAction\nimport com.intellij.openapi.util.Segment\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiFile\nimport com.jetbrains.php.lang.PhpLangUtil\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.PhpPsiElement\nimport com.jetbrains.php.lang.psi.elements.PhpTypedElement\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.PestSettings\nimport java.util.regex.Pattern\n\nclass PestTypeInferenceTest : PestLightCodeFixture() {\n    private lateinit var pestFilePathBackup: String\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/codeInsight/typeInference\"\n\n    private fun doTest(block: () -> PsiFile) {\n        PestSettings.getInstance(project).pestFilePath = \"Pest.php\"\n        val pestPhpFile = block()\n        myFixture.openFileInEditor(pestPhpFile.virtualFile)\n        doTypeTest()\n    }\n\n    private fun doTypeTest() {\n        val file = configureByFile()\n        val originalFileText = file.text\n        val fileTextWithExtractedTypes = StringBuilder()\n        val rangesToTypes = extractRangesToTypes(originalFileText, fileTextWithExtractedTypes)\n        val fileWithExtractedTypes = replaceContentOfConfiguredFile(fileTextWithExtractedTypes)\n        val text = fileWithExtractedTypes.text\n        var lastOffset = 0\n        val contentWithActualTypes = StringBuilder()\n        for ((segment, stringTypes) in rangesToTypes.entries) {\n            val actualType = getType(segment.startOffset, segment.endOffset)\n            if (actualType.hasUnknown()) {\n                actualType.add(PhpType.MIXED)\n            }\n            val diffType = constructDiffType(actualType, stringTypes)\n            contentWithActualTypes.append(text, lastOffset, segment.startOffset)\n            appendActualType(text, contentWithActualTypes, segment, diffType)\n            lastOffset = segment.endOffset\n        }\n        contentWithActualTypes.append(text.substring(lastOffset))\n        assertEquals(\"Types are not matched\", originalFileText, contentWithActualTypes.toString())\n    }\n\n    private fun replaceContentOfConfiguredFile(fileTextWithExtractedTypes: StringBuilder): PsiFile {\n        WriteAction.run<RuntimeException> { myFixture.editor.getDocument().setText(fileTextWithExtractedTypes.toString()) }\n        PsiDocumentManager.getInstance(project).commitAllDocuments()\n        return myFixture.file\n    }\n\n    private fun getType(start: Int, end: Int): PhpType {\n        val expression = PhpPsiUtil.findElementOfClassAtRange(\n            myFixture.file, start, end,\n            PhpPsiElement::class.java, true, true\n        )\n        assertNotNull(expression)\n        assertTrue(expression!!.text, expression is PhpTypedElement)\n        return (expression as PhpTypedElement?)!!.globalType\n    }\n\n    fun testThisInInnerClosure() = doTest {\n        myFixture.addFileToProject(\"Pest.php\", \"<?php\")\n    }\n\n    fun `testThisInSubproject#Test`() = doTest {\n        PestSettings.getInstance(project).pestFilePath = \"ThisInSubproject/Pest.php\"\n        myFixture.addFileToProject(\n            \"ThisInSubproject/TestCase.php\", \"\"\"\n            <?php\n\n            abstract class TestCase { }\n            \"\"\"\n        )\n        myFixture.addFileToProject(\n            \"ThisInSubproject/Pest.php\", \"\"\"\n            <?php\n                        \n            uses(TestCase::class)->in(\"./\");\n        \"\"\".trimIndent()\n        )\n    }\n\n    override fun setUp() {\n        super.setUp()\n        pestFilePathBackup = PestSettings.getInstance(project).pestFilePath\n    }\n\n    override fun tearDown() {\n        try {\n            PestSettings.getInstance(project).pestFilePath = pestFilePathBackup\n        } catch (e: Throwable) {\n            addSuppressedException(e)\n        } finally {\n            super.tearDown()\n        }\n    }\n}\n\nprivate const val CLOSING_TYPE_TAG = \"</type>\"\nprivate val PATTERN = Pattern.compile(\"<type value=\\\"([^\\\"]*)\\\">\")\n\nprivate fun extractRangesToTypes(text: String, contentWithExtractedTypes: java.lang.StringBuilder): LinkedHashMap<Segment, List<String>> {\n    val res = LinkedHashMap<Segment, List<String>>()\n    var lastOffset = 0\n    val matcher = PATTERN.matcher(text)\n    while (matcher.find()) {\n        contentWithExtractedTypes.append(text, lastOffset, matcher.start())\n        val closingTagIndexOf = text.indexOf(CLOSING_TYPE_TAG, lastOffset)\n        val start = contentWithExtractedTypes.length\n        contentWithExtractedTypes.append(text, matcher.end(), closingTagIndexOf)\n        val end = contentWithExtractedTypes.length\n        res[TextRange.create(start, end)] = StringUtil.split(matcher.group(1), \"|\")\n        lastOffset = closingTagIndexOf + CLOSING_TYPE_TAG.length\n    }\n    contentWithExtractedTypes.append(text.substring(lastOffset))\n    return res\n}\n\nprivate fun constructDiffType(type: PhpType, strings: List<String>): String {\n    return type.types.asSequence()\n        .filter { t -> !t.startsWith(\"?\") }\n        .filter { qualifiedName -> PhpLangUtil.isFqn(qualifiedName!!) }\n        .map { fqn -> toPresentableFQN(fqn) }\n        .sortedBy { fqn -> strings.indexOf(fqn) }\n        .joinToString(\"|\")\n}\n\nprivate fun toPresentableFQN(fqn: String): String {\n    return if (PhpLangUtil.isGlobalNamespaceFQN(PhpLangUtil.getParentNamespaceFQN(fqn))) PhpLangUtil.toPresentableFQN(fqn) else fqn\n}\n\nprivate fun appendActualType(text: String, contentWithActualTypes: java.lang.StringBuilder, segment: Segment, diffType: String) {\n    contentWithActualTypes.append(\"<type value=\\\"\").append(diffType).append(\"\\\">\")\n    contentWithActualTypes.append(text, segment.startOffset, segment.endOffset)\n    contentWithActualTypes.append(CLOSING_TYPE_TAG)\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/configuration/PestLocationProviderTest.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.testFramework.PlatformTestUtil\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.phpunit.PhpPsiLocationWithDataSet\nimport com.jetbrains.php.util.pathmapper.PhpPathMapper\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestLocationProviderTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/configuration/locationProvider\"\n\n    fun testSubprojectFor2xVersion() {\n        val testName = \"test\"\n        doTestGetLocation(\n            \"Test.php::$testName\", testName, \"subdir\"\n        )\n    }\n\n    fun testSubprojectFor1xVersion() {\n        val testName = \"test\"\n        doTestGetLocation(\n            \"Test.php::$testName\", testName, \"subdir\", true\n        )\n    }\n\n    fun testDescribeBlock() {\n        doTestGetLocation(\n            \"Test.php::`sum` → check true\", \"test\", \"subdir\"\n        )\n    }\n\n    fun testDescribeBlockIt() {\n        doTestGetLocation(\n            \"Test.php::`sum` → it check valid\", \"it\", \"subdir\"\n        )\n    }\n\n    private fun doTestGetLocation(\n        pathSuffix: String,\n        expectedTestName: String,\n        configurationFileRelativePath: String? = null,\n        isAbsolutePath: Boolean = false,\n    ) {\n        val testDir = PlatformTestUtil.getTestName(name, false)\n        myFixture.copyDirectoryToProject(testDir, \".\")\n        val configurationFileRootPath = \"${myFixture.testDataPath}/${testDir}${configurationFileRelativePath?.let { \"/$it\" } ?: \"\"}\"\n        val locationProvider = PestLocationProvider(PhpPathMapper.create(project), project, configurationFileRootPath)\n        val path = if (isAbsolutePath) \"$configurationFileRootPath/$pathSuffix\" else pathSuffix\n        val resolvedLocation = locationProvider\n            .getLocation(\"pest_qn\", path, project, GlobalSearchScope.allScope(project)).firstOrNull()\n\n        assertInstanceOf(resolvedLocation, PhpPsiLocationWithDataSet::class.java)\n        assertInstanceOf(resolvedLocation?.psiElement, FunctionReference::class.java)\n        assertEquals((resolvedLocation?.getPsiElement() as? FunctionReference)?.name, expectedTestName)\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/configuration/PestRunConfigurationTest.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.PsiLocation\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.jetbrains.php.config.commandLine.PhpCommandSettings\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl\nimport com.jetbrains.php.phpunit.coverage.PhpUnitCoverageEngine\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.PestFrameworkType\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestRunConfigurationTest : PestLightCodeFixture() {\n    private lateinit var configurationsBackup: List<PhpTestFrameworkConfiguration>\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/configuration\"\n\n    fun testRunConfigurationRunnerSettingsIsPestRunnerSettings() {\n        val configuration = createConfiguration()\n\n        assertNotNull(configuration)\n        assertInstanceOf(configuration.settings, PestRunConfigurationSettings::class.java)\n        assertInstanceOf(configuration.settings.runnerSettings, PestRunnerSettings::class.java)\n    }\n\n    fun testRunConfigurationClone() {\n        val configuration = createConfiguration()\n        configuration.pestSettings.pestRunnerSettings.coverageEngine = PhpUnitCoverageEngine.CoverageEngine.PCOV\n        configuration.pestSettings.pestRunnerSettings.parallelTestingEnabled = true\n\n        val clone = configuration.clone() as PestRunConfiguration\n        assertEquals(PhpUnitCoverageEngine.CoverageEngine.PCOV, clone.pestSettings.pestRunnerSettings.coverageEngine)\n        assertEquals(true, clone.pestSettings.pestRunnerSettings.parallelTestingEnabled)\n    }\n\n    fun testRunConfigurationCommand() {\n        val command = createCommand()\n        val expectedCommand = \"randomPath --teamcity /src/FileWithPestTest.php\"\n        assertEquals(expectedCommand, command.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    fun testRunConfigurationCommandWithEnabledParallelTesting() {\n        val configuration = createConfiguration()\n        configuration.pestSettings.pestRunnerSettings.parallelTestingEnabled = true\n        val command = configuration.createCommand(configuration.interpreter!!, mutableMapOf(), mutableListOf(), false)\n\n        val expectedCommand = \"randomPath --teamcity /src/FileWithPestTest.php --parallel --log-teamcity php://stdout\"\n        assertEquals(expectedCommand, command.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    private fun createCommand(): PhpCommandSettings {\n        val configuration = createConfiguration()\n\n        return configuration.createCommand(configuration.interpreter!!, mutableMapOf(), mutableListOf(), false)\n    }\n\n    private fun createConfiguration(): PestRunConfiguration {\n        val file = myFixture.configureByFile(\"FileWithPestTest.php\")\n        val elementAtCaret = file.findElementAt(myFixture.editor.caretModel.offset)\n\n        createPestFrameworkConfiguration()\n\n        val configuration = PestRunConfigurationProducer().createConfigurationFromContext(\n            ConfigurationContext.createEmptyContextForLocation(\n                PsiLocation.fromPsiElement(elementAtCaret)\n            )\n        )?.configuration as PestRunConfiguration\n        configuration.settings.commandLineSettings.interpreterSettings.interpreterName = getTestName(false)\n        return configuration\n    }\n\n    override fun setUp() {\n        super.setUp()\n        val interpreter = PhpInterpreter().apply {\n            name = getTestName(false)\n            homePath = \"$testDataPath/php\"\n        }\n        PhpInterpretersManagerImpl.getInstance(project).addInterpreter(interpreter)\n        configurationsBackup = PhpTestFrameworkSettingsManager.getInstance(project).getConfigurations(PestFrameworkType.instance)\n    }\n\n    override fun tearDown() {\n        try {\n            PhpTestFrameworkSettingsManager.getInstance(project).setConfigurations(PestFrameworkType.instance, configurationsBackup)\n            PhpInterpretersManagerImpl.getInstance(project).interpreters = emptyList()\n        } catch (e: Throwable) {\n            addSuppressedException(e)\n        } finally {\n            super.tearDown()\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/configuration/PestVersionDetectorTest.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.ExecutionException\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestLightCodeFixture\nimport io.mockk.every\nimport io.mockk.mockk\n\nclass PestVersionDetectorTest : PestLightCodeFixture() {\n    fun testThrowsOnRemoteInterpreter() {\n        val versionDetector = PestVersionDetector()\n        val mockInterpreter = mockk<PhpInterpreter>()\n        every { mockInterpreter.isRemote } returns true\n\n        assertThrows(\n            ExecutionException::class.java,\n            PestBundle.message(\"PEST_VERSION_IS_NOT_SUPPORTED_FOR_REMOTE_INTERPRETER\")\n        ) {\n            versionDetector.getVersion(project, mockInterpreter, null)\n        }\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/configuration/PestVersionParserTest.kt",
    "content": "package com.pestphp.pest.configuration\n\nimport com.intellij.execution.ExecutionException\nimport com.pestphp.pest.PestBundle\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestVersionParserTest : PestLightCodeFixture() {\n    private fun doTest(output: String, expected: String) {\n        val version = PestVersionDetector.instance.parse(output)\n        assertNotNull(version)\n        assertEquals(version, expected)\n    }\n\n    private fun doFailedTest(output: String) {\n        assertThrows(ExecutionException::class.java, PestBundle.message(\"PEST_CONFIGURATION_UI_CAN_NOT_PARSE_VERSION\", output)) {\n            PestVersionDetector.instance.parse(output)\n        }\n    }\n\n    fun testPestOutputBeforeV2() {\n        doTest(\"\"\"\n            Pest    1.21.0\n            PHPUnit 9.6.15\n        \"\"\".trimIndent(), \"1.21.0\")\n    }\n\n    fun testPestOutputAfterV2() {\n        doTest(\"\"\"\n            \n              Pest Testing Framework 2.21.0.  \n\n        \"\"\".trimIndent(), \"2.21.0\")\n    }\n\n    fun testIncorrectFormatBeforeV2() {\n        doFailedTest(\"Some text 1.21.0\")\n    }\n\n    fun testIncorrectFormatAfterV2() {\n        doFailedTest(\"\"\"\n            \n              Pest 2.21.0.  \n\n        \"\"\".trimIndent())\n    }\n\n    fun testIncorrectVersionFormat() {\n        doFailedTest(\"Pest 1.21\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/configuration/pest/PestConfigurationFileTest.kt",
    "content": "package com.pestphp.pest.configuration.pest\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestConfigurationFileTest : PestLightCodeFixture() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyDirectoryToProject(\".\", \".\")\n    }\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/configuration/pest\"\n\n    fun testUnit() {\n        myFixture.configureByFile(\"tests/Unit/UnitTest.php\")\n\n        assertCompletion(\"baseTestFunc\")\n    }\n\n    fun testPestExtendUsesUnit() {\n        myFixture.configureByFile(\"tests/Unit/PestExtendUnitTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"traitFunc\")\n    }\n\n    fun testFeature() {\n        myFixture.configureByFile(\"tests/Feature/FeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGroupedFeature() {\n        myFixture.configureByFile(\"tests/GroupedFeature/GroupedFeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\", \"someBaseTraitFunc\")\n    }\n\n    fun testDIRFeature() {\n        myFixture.configureByFile(\"tests/DIRFeature/FeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testDynamicFolderFeature() {\n        myFixture.configureByFile(\"tests/DynamicFeature/FeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testUnitOutsideOfPestTestDir() {\n        myFixture.configureByFile(\"tests2/Unit/UnitTest.php\")\n\n        assertCompletion()\n    }\n\n    fun testGlobPatternDirectory() {\n        myFixture.configureByFile(\"tests/GlobPattern/DirectoryTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGlobPatternFile() {\n        myFixture.configureByFile(\"tests/GlobPattern/FileTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGlobPatternFileWithRelativePath() {\n        myFixture.configureByFile(\"tests/GlobPattern/FileWithRelativePathTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/configuration/uses/PestConfigurationFileTest.kt",
    "content": "package com.pestphp.pest.configuration.uses\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestConfigurationFileTest : PestLightCodeFixture() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyDirectoryToProject(\".\", \"tests\")\n    }\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/configuration/uses\"\n\n    fun testUnit() {\n        myFixture.configureByFile(\"tests/Unit/UnitTest.php\")\n\n        assertCompletion(\"baseTestFunc\")\n    }\n\n    fun testUsesUnit() {\n        myFixture.configureByFile(\"tests/Unit/UsesUnitTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"traitFunc\")\n    }\n\n    fun testFeature() {\n        myFixture.configureByFile(\"tests/Feature/FeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGroupedFeature() {\n        myFixture.configureByFile(\"tests/GroupedFeature/GroupedFeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\", \"someBaseTraitFunc\")\n    }\n\n    fun testDIRFeature() {\n        myFixture.configureByFile(\"tests/DIRFeature/FeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testDynamicFolderFeature() {\n        myFixture.configureByFile(\"tests/DynamicFeature/FeatureTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGlobPatternDirectory() {\n        myFixture.configureByFile(\"tests/GlobPattern/DirectoryTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGlobPatternFile() {\n        myFixture.configureByFile(\"tests/GlobPattern/FileTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n\n    fun testGlobPatternFileWithRelativePath() {\n        myFixture.configureByFile(\"tests/GlobPattern/FileWithRelativePathTest.php\")\n\n        assertCompletion(\"baseTestFunc\", \"featureTestFunc\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/customExpectations/ListMethodDataExternalizerTest.kt",
    "content": "package com.pestphp.pest.customExpectations\n\nimport com.intellij.util.io.DataOutputStream\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.features.customExpectations.externalizers.ListDataExternalizer\nimport com.pestphp.pest.features.customExpectations.externalizers.MethodDataExternalizer\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport com.pestphp.pest.features.customExpectations.generators.Parameter\nimport java.io.ByteArrayInputStream\nimport java.io.ByteArrayOutputStream\nimport java.io.DataInputStream\n\nclass ListMethodDataExternalizerTest : PestLightCodeFixture() {\n    private fun saveAndRead(method: List<Method>): List<Method> {\n        val dataOutput = ByteArrayOutputStream()\n\n        val externalizer = ListDataExternalizer(MethodDataExternalizer.INSTANCE)\n        externalizer.save(\n            DataOutputStream(dataOutput),\n            method\n        )\n\n        val dataInput = DataInputStream(\n            ByteArrayInputStream(dataOutput.toByteArray())\n        )\n\n        return externalizer.read(dataInput)\n    }\n\n    fun testCanExternalizeMethod() {\n        val methods = listOf(\n            Method(\n                \"works\",\n                PhpType.BOOLEAN,\n                emptyList()\n            )\n        )\n\n       val deserializedMethods = saveAndRead(methods)\n\n        assertEquals(\n            methods,\n            deserializedMethods\n        )\n    }\n\n    fun testCanExternalizeMultipleMethods() {\n        val methods = listOf(\n            Method(\n                \"works\",\n                PhpType.BOOLEAN,\n                emptyList()\n            ),\n            Method(\n                \"alsoWorks\",\n                PhpType.BOOLEAN,\n                emptyList()\n            )\n        )\n\n        val deserializedMethods = saveAndRead(methods)\n\n        assertEquals(\n            methods,\n            deserializedMethods\n        )\n    }\n\n    fun testCanExternalizeMethodsWithMultipleTypes() {\n        val methods = listOf(\n            Method(\n                \"works\",\n                PhpType.builder().add(\"random\").add(PhpType.EXCEPTION).build(),\n                emptyList()\n            ),\n            Method(\n                \"valid\",\n                PhpType.builder().add(\"some\").add(PhpType.STRING).build(),\n                emptyList()\n            )\n        )\n\n        val deserializedMethods = saveAndRead(methods)\n\n        assertEquals(\n            methods,\n            deserializedMethods\n        )\n    }\n\n    fun testCanExternalizeMethodsWithParameter() {\n        val methods = listOf(\n            Method(\n                \"getName\",\n                PhpType.BOOLEAN,\n                listOf(\n                    Parameter(\n                        \"name\",\n                        PhpType.STRING,\n                    )\n                )\n            ),\n            Method(\n                \"somethingElse\",\n                PhpType.BOOLEAN,\n                listOf()\n            ),\n            Method(\n                \"alsoWorks\",\n                PhpType.BOOLEAN,\n                listOf(\n                    Parameter(\n                        \"value\",\n                        PhpType.BOOLEAN,\n                    )\n                )\n            ),\n        )\n\n        val deserializedMethods = saveAndRead(methods)\n\n        assertEquals(\n            methods,\n            deserializedMethods\n        )\n    }\n\n    fun testCanExternalizeMethodWithParameterWithDefaultValue() {\n        val methods = listOf(\n            Method(\n                \"getName\",\n                PhpType.BOOLEAN,\n                listOf(\n                    Parameter(\n                        \"name\",\n                        PhpType.STRING,\n                        \"'Oliver'\"\n                    )\n                )\n            ),\n            Method(\n                \"otherMethod\",\n                PhpType.BOOLEAN,\n                listOf(\n                    Parameter(\n                        \"name\",\n                        PhpType.STRING,\n                        \"true\"\n                    )\n                )\n            )\n        )\n\n        val deserializedMethods = saveAndRead(methods)\n\n        assertEquals(\n            methods,\n            deserializedMethods\n        )\n    }\n\n    fun testCanExternalizeMethodWithMultipleParameters() {\n        val methods = listOf(\n            Method(\n                \"someMethod\",\n                PhpType.BOOLEAN,\n                listOf(\n                    Parameter(\n                        \"parameter1\",\n                        PhpType.STRING,\n                    ),\n                    Parameter(\n                        \"otherParameter\",\n                        PhpType.STRING,\n                        \"'someValue'\"\n                    )\n                )\n            ),\n            Method(\n                \"andAnotherOne\",\n                PhpType.EXCEPTION,\n                listOf(\n                    Parameter(\n                        \"parameter3\",\n                        PhpType.STRING,\n                    ),\n                    Parameter(\n                        \"otherParameter2\",\n                        PhpType.STRING,\n                        \"false\"\n                    )\n                )\n            )\n        )\n\n        val deserializedMethods = saveAndRead(methods)\n\n        assertEquals(\n            methods,\n            deserializedMethods\n        )\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/customExpectations/MethodDataExternalizerTest.kt",
    "content": "package com.pestphp.pest.customExpectations\n\nimport com.intellij.util.io.DataOutputStream\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.features.customExpectations.externalizers.MethodDataExternalizer\nimport com.pestphp.pest.features.customExpectations.generators.Method\nimport com.pestphp.pest.features.customExpectations.generators.Parameter\nimport java.io.ByteArrayInputStream\nimport java.io.ByteArrayOutputStream\nimport java.io.DataInputStream\n\nclass MethodDataExternalizerTest : PestLightCodeFixture() {\n    private fun saveAndRead(method: Method): Method {\n        val dataOutput = ByteArrayOutputStream()\n\n        val externalizer = MethodDataExternalizer()\n        externalizer.save(\n            DataOutputStream(dataOutput),\n            method\n        )\n\n        val dataInput = DataInputStream(\n            ByteArrayInputStream(dataOutput.toByteArray())\n        )\n\n        return externalizer.read(dataInput)\n    }\n\n    fun testCanExternalizeMethod() {\n        val method = Method(\n            \"works\",\n            PhpType.BOOLEAN,\n            emptyList()\n        )\n\n       val deserializedMethod = saveAndRead(method)\n\n        assertEquals(\n            method,\n            deserializedMethod\n        )\n    }\n\n    fun testCanExternalizeMethodWithMultipleTypes() {\n        val method = Method(\n            \"works\",\n            PhpType.builder().add(\"random\").add(PhpType.EXCEPTION).build(),\n            emptyList()\n        )\n\n        val deserializedMethod = saveAndRead(method)\n\n        assertEquals(\n            method,\n            deserializedMethod\n        )\n    }\n\n    fun testCanExternalizeMethodWithParameter() {\n        val method = Method(\n            \"getName\",\n            PhpType.BOOLEAN,\n            listOf(\n                Parameter(\n                    \"name\",\n                    PhpType.STRING,\n                )\n            )\n        )\n\n        val deserializedMethod = saveAndRead(method)\n\n        assertEquals(\n            method,\n            deserializedMethod\n        )\n    }\n\n    fun testCanExternalizeMethodWithParameterWithDefaultValue() {\n        val method = Method(\n            \"getName\",\n            PhpType.BOOLEAN,\n            listOf(\n                Parameter(\n                    \"name\",\n                    PhpType.STRING,\n                    \"'Oliver'\"\n                )\n            )\n        )\n\n        val deserializedMethod = saveAndRead(method)\n\n        assertEquals(\n            method,\n            deserializedMethod\n        )\n    }\n\n    fun testCanExternalizeMethodWithMultipleParameters() {\n        val method = Method(\n            \"someMethod\",\n            PhpType.BOOLEAN,\n            listOf(\n                Parameter(\n                    \"parameter1\",\n                    PhpType.STRING,\n                ),\n                Parameter(\n                    \"otherParameter\",\n                    PhpType.STRING,\n                    \"'someValue'\"\n                )\n            )\n        )\n\n        val deserializedMethod = saveAndRead(method)\n\n        assertEquals(\n            method,\n            deserializedMethod\n        )\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/customExpectations/generators/ExpectationGeneratorTest.kt",
    "content": "package com.pestphp.pest.customExpectations.generators\n\nimport com.jetbrains.php.lang.psi.resolve.types.PhpType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.features.customExpectations.generators.ExpectationGenerator\nimport com.pestphp.pest.features.customExpectations.generators.Method\n\nclass ExpectationGeneratorTest : PestLightCodeFixture() {\n    fun testCanGenerateFileWithMethod() {\n        val generator = ExpectationGenerator()\n        generator.docMethods.add(\n            Method(\n                \"works\",\n                PhpType.BOOLEAN,\n                emptyList()\n            )\n        )\n\n        val result = generator.generate(project)\n\n        val expectedClass = ExpectationGeneratorTest::class.java\n            .getResource(\"/com/pestphp/pest/customExpectations/generators/ExpectationGenerator/GeneratedWithMethod.php\")\n            ?.readText() ?: fail(\"File not found.\")\n        assertEquals(\n            result,\n            expectedClass\n        )\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/configuration/PestCompletionTest.kt",
    "content": "package com.pestphp.pest.features.configuration\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/configuration/pest\")\nclass PestCompletionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/configuration/pest\"\n\n    fun testCanCompleteSubFolder() {\n        myFixture.copyFileToProject(\"Test.php\", \"myFolder/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"other/Test.php\")\n        myFixture.configureByFile(\"CompleteInFolder.php\")\n\n        assertCompletion(\"myFolder\", \"other\")\n    }\n\n    fun testDoesNotCompleteInNotPestInStatement() {\n        myFixture.copyFileToProject(\"Test.php\", \"myFolder/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"other/Test.php\")\n        myFixture.configureByFile(\"CompleteFakePestInFolder.php\")\n\n        assertNoCompletion()\n    }\n\n    fun testOnlyCompletesFoldersUnderFile() {\n        myFixture.copyFileToProject(\"Test.php\", \"myFolder/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"tests/Feature/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"tests/Unit/Test.php\")\n        val virtualFile = myFixture.copyFileToProject(\"CompleteInFolder.php\", \"tests/Pest.php\")\n        myFixture.configureFromExistingVirtualFile(virtualFile)\n\n        assertAllCompletion(\"Feature\", \"Unit\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/configuration/UsesCompletionTest.kt",
    "content": "package com.pestphp.pest.features.configuration\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/configuration/uses\")\nclass UsesCompletionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/configuration/uses\"\n\n    fun testCanCompleteSubFolder() {\n        myFixture.copyFileToProject(\"Test.php\", \"myFolder/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"other/Test.php\")\n        myFixture.configureByFile(\"CompleteInFolder.php\")\n\n        assertCompletion(\"myFolder\", \"other\")\n    }\n\n    fun testDoesNotCompleteInNotUsesInStatement() {\n        myFixture.copyFileToProject(\"Test.php\", \"myFolder/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"other/Test.php\")\n        myFixture.configureByFile(\"CompleteFakeInFolder.php\")\n\n        assertNoCompletion()\n    }\n\n    fun testOnlyCompletesFoldersUnderFile() {\n        myFixture.copyFileToProject(\"Test.php\", \"myFolder/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"tests/Feature/Test.php\")\n        myFixture.copyFileToProject(\"Test.php\", \"tests/Unit/Test.php\")\n        val virtualFile = myFixture.copyFileToProject(\"CompleteInFolder.php\", \"tests/Pest.php\")\n        myFixture.configureFromExistingVirtualFile(virtualFile)\n\n        assertAllCompletion(\"Feature\", \"Unit\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/DatasetCompletionTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/datasets\")\nclass DatasetCompletionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/datasets\"\n\n    fun testCanCompleteDatasetInSameFile() {\n        myFixture.configureByFile(\"AutocompleteDatasetTest.php\")\n\n        assertCompletion(\"dojos\")\n    }\n\n    fun testCanCompleteDatasetInOtherFile() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        myFixture.configureByFile(\"AutocompleteDatasetTest.php\")\n\n        assertCompletion(\"dojos\", \"stances\")\n    }\n\n    fun testCannotCompleteDatasetOnNonPestTest() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        myFixture.configureByFile(\"DatasetOnNonPestTestCompletion.php\")\n\n        assertNoCompletion()\n    }\n\n    fun testCanCompleteDatasetInDescribeBlock() {\n        myFixture.configureByFile(\"DatasetInDescribeBlockCompletion.php\")\n\n        assertCompletion(\"some_numbers\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/DatasetGoToTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.codeInsight.navigation.actions.GotoDeclarationAction\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/datasets\")\nclass DatasetGoToTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/datasets\"\n\n    fun testCanGoToDatasetInSameFile() {\n        myFixture.configureByFile(\"DatasetReference.php\")\n\n        val declarationElement = GotoDeclarationAction.findTargetElement(\n            project,\n            myFixture.editor,\n            myFixture.caretOffset\n        ) as? FunctionReferenceImpl\n\n        assertNotNull(declarationElement)\n        assertTrue(declarationElement.isPestDataset())\n        assertEquals(\"dojos\", declarationElement!!.getPestDatasetName())\n    }\n\n    fun testCanGoToDatasetInOtherFile() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        myFixture.configureByFile(\"SharedDatasetReference.php\")\n\n        val declarationElement = GotoDeclarationAction.findTargetElement(\n            project,\n            myFixture.editor,\n            myFixture.caretOffset\n        ) as? FunctionReferenceImpl\n\n        assertNotNull(declarationElement)\n        assertTrue(declarationElement.isPestDataset())\n        assertEquals(\"stances\", declarationElement!!.getPestDatasetName())\n    }\n\n    fun testCannotGoToDatasetInNonPestTest() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        myFixture.configureByFile(\"DatasetOnNonPestTest.php\")\n\n        val declarationElement = GotoDeclarationAction.findTargetElement(\n            project,\n            myFixture.editor,\n            myFixture.caretOffset\n        )\n\n        assertNull(declarationElement)\n    }\n\n    fun testCanGoToDatasetInDescribeBlock() {\n        myFixture.configureByFile(\"DatasetInDescribeBlockReference.php\")\n\n        val declarationElement = GotoDeclarationAction.findTargetElement(\n            project,\n            myFixture.editor,\n            myFixture.caretOffset\n        ) as? FunctionReferenceImpl\n\n        assertNotNull(declarationElement)\n        assertTrue(declarationElement.isPestDataset())\n        assertEquals(\"some_numbers\", declarationElement!!.getPestDatasetName())\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/DatasetIndexTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.testFramework.TestDataPath\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.pestphp.pest.PestLightCodeFixture\nimport junit.framework.TestCase\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/datasets\")\nclass DatasetIndexTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/datasets\"\n\n    fun testDatasetIsIndexed() {\n        myFixture.copyFileToProject(\n            \"Datasets.php\",\n            \"/tests/Datasets/Datasets.php\"\n        )\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val values = fileBasedIndex.getValues(\n          key,\n          \"/src/tests/Datasets/Datasets.php\",\n          GlobalSearchScope.projectScope(project)\n        ).flatten()\n\n        assertSize(1, values)\n        TestCase.assertEquals(\n            listOf(\"stances\"),\n            values\n        )\n    }\n\n    fun testDatasetIsNotIndexedWhenOutsideDatasetFolder() {\n        val virtualFile = myFixture.copyFileToProject(\n            \"Datasets.php\",\n            \"/tests/Datasets.php\"\n        )\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val indexData = fileBasedIndex.getFileData(\n          key,\n          virtualFile,\n          project\n        )\n\n        assertEquals(0, indexData.count())\n    }\n\n    fun testDatasetInsideDescribeBlockIsIndexed() {\n        myFixture.copyFileToProject(\n            \"DatasetInDescribeBlock.php\",\n            \"/tests/Datasets/DatasetInDescribeBlock.php\"\n        )\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val values = fileBasedIndex.getValues(\n            key,\n            \"/src/tests/Datasets/DatasetInDescribeBlock.php\",\n            GlobalSearchScope.projectScope(project)\n        ).flatten()\n\n        assertSize(2, values)\n        assertContainsElements(values, \"top_level\", \"inside_describe\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/DatasetReferenceTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/datasets\")\nclass DatasetReferenceTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/datasets\"\n\n    fun testReferenceToDatasetInSameFile() {\n        val file = myFixture.configureByFile(\"DatasetReference.php\")\n\n        val caretElement = file.findElementAt(myFixture.caretOffset) ?: return fail(\"No element\")\n        val datasetReference = caretElement.parent.references.filterIsInstance<DatasetReference>().firstOrNull() ?: return fail(\"No reference\")\n        val resolved = datasetReference.resolve() as? FunctionReferenceImpl ?: return fail(\"No function\")\n\n        assertTrue(resolved.isPestDataset())\n        assertEquals(\"dojos\", resolved.getPestDatasetName())\n    }\n\n    fun testReferenceDatasetInOtherFile() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        val file = myFixture.configureByFile(\"SharedDatasetReference.php\")\n\n        val caretElement = file.findElementAt(myFixture.caretOffset) ?: return fail(\"No element\")\n        val datasetReference = caretElement.parent.references.filterIsInstance<DatasetReference>().firstOrNull() ?: return fail(\"No reference\")\n        val resolved = datasetReference.resolve() as? FunctionReferenceImpl ?: return fail(\"No function\")\n\n        assertTrue(resolved.isPestDataset())\n        assertEquals(\"stances\", resolved.getPestDatasetName())\n    }\n\n    fun testDoubleWith() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        myFixture.configureByFile(\"DoubleWithDatasetReference.php\")\n\n        assertCompletion(\"stances\")\n    }\n\n    fun testNotDataset() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        myFixture.configureByFile(\"NotDatasetReference.php\")\n\n        assertNoCompletion()\n    }\n\n    fun testCannotGoToDatasetInNonPestTest() {\n        myFixture.copyFileToProject(\"Datasets.php\", \"tests/Datasets/stances.php\")\n        val file = myFixture.configureByFile(\"DatasetOnNonPestTest.php\")\n\n        val caretElement = file.findElementAt(myFixture.caretOffset) ?: return fail(\"No element\")\n        val datasetReference = caretElement.parent.references.filterIsInstance<DatasetReference>()\n\n        assertSize(0, datasetReference)\n    }\n\n    fun testReferenceToDatasetInDescribeBlock() {\n        val file = myFixture.configureByFile(\"DatasetInDescribeBlockReference.php\")\n\n        val caretElement = file.findElementAt(myFixture.caretOffset) ?: return fail(\"No element\")\n        val datasetReference = caretElement.parent.references.filterIsInstance<DatasetReference>().firstOrNull() ?: return fail(\"No reference\")\n        val resolved = datasetReference.resolve() as? FunctionReferenceImpl ?: return fail(\"No function\")\n\n        assertTrue(resolved.isPestDataset())\n        assertEquals(\"some_numbers\", resolved.getPestDatasetName())\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/DatasetUsagesTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.lang.psi.elements.MethodReference\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.goto.PestDatasetUsagesGotoHandler\nimport junit.framework.TestCase\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/goto/datasetUsages\")\nclass DatasetUsagesTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/goto/datasetUsages\"\n\n    fun testGotoUsages() {\n        myFixture.copyFileToProject(\"DatasetUsage.php\", \"tests/usages.php\")\n        myFixture.copyFileToProject(\"DatasetDeclaration.php\", \"tests/Datasets/declaration.php\")\n        val file = myFixture.configureFromTempProjectFile(\"tests/Datasets/declaration.php\")\n\n        val element = file.findElementAt(myFixture.caretOffset) ?: return fail(\"No element\")\n\n        val usages = PestDatasetUsagesGotoHandler().getGotoDeclarationTargets(element, 0, null) ?: return fail(\"No usages\")\n\n        assertEquals(2, usages.size)\n        TestCase.assertTrue(usages.any { it is MethodReference && it.containingFile?.name == \"usages.php\" })\n        TestCase.assertTrue(usages.any { it is MethodReference && it.containingFile?.name == \"declaration.php\" })\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/InvalidDatasetNameCaseInspectionTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/datasets\")\nclass InvalidDatasetNameCaseInspectionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/datasets\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.enableInspections(InvalidDatasetNameCaseInspection::class.java)\n    }\n\n    fun testInvalidDatasetNameCase() {\n        myFixture.configureByFile(\"InvalidDatasetNameCase.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n        myFixture.checkResultByFile(\"InvalidDatasetNameCase.after.php\")\n    }\n\n    fun testValidDatasetNameCase() {\n        myFixture.configureByFile(\"ValidDatasetNameCase.php\")\n\n        myFixture.checkHighlighting()\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/datasets/InvalidDatasetReferenceInspectionTest.kt",
    "content": "package com.pestphp.pest.features.datasets\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/datasets\")\nclass InvalidDatasetReferenceInspectionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/datasets\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.enableInspections(InvalidDatasetReferenceInspection::class.java)\n    }\n\n    fun testHasInvalidDatasetName() {\n        myFixture.configureByFile(\"InvalidDatasetTest.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testHasValidDataset() {\n        myFixture.configureByFile(\"DatasetTest.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testHasWithStatementWithNoArgs() {\n        myFixture.configureByFile(\"DatasetNoArgsTest.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testHasInvalidDatasetInDescribeBlock() {\n        myFixture.configureByFile(\"InvalidDatasetInDescribeBlockTest.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testDatasetInsideDescribeBlock() {\n        myFixture.configureByFile(\"DatasetInsideDescribeBlockTest.php\")\n\n        myFixture.checkHighlighting()\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/parallel/PestParallelProgramRunnerTest.kt",
    "content": "package com.pestphp.pest.features.parallel\n\nimport com.intellij.execution.PsiLocation\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.psi.PsiElement\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.config.interpreters.PhpInterpreter\nimport com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl\nimport com.jetbrains.php.testFramework.PhpTestFrameworkConfiguration\nimport com.jetbrains.php.testFramework.PhpTestFrameworkSettingsManager\nimport com.pestphp.pest.PestFrameworkType\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.configuration.PestRunConfigurationProducer\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/parallel\")\nclass PestParallelProgramRunnerTest : PestLightCodeFixture() {\n    private lateinit var configurationsBackup: List<PhpTestFrameworkConfiguration>\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/parallel\"\n\n    fun testCannotRunWrongExecutorId() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        assertFalse(PestParallelProgramRunner().canRun(PestParallelTestExecutor.EXECUTOR_ID + \"1\", configuration))\n    }\n\n    fun testCanRunFile() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n        assertTrue(PestParallelProgramRunner().canRun(PestParallelTestExecutor.EXECUTOR_ID, configuration))\n    }\n\n    fun testCanRunFunction() = doTest {\n        val testElement = myFixture.file?.firstChild?.lastChild?.firstChild ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        assertTrue(PestParallelProgramRunner().canRun(PestParallelTestExecutor.EXECUTOR_ID, configuration))\n    }\n\n    fun testCanRunDirectory() = doTest {\n        val testElement = myFixture.file?.containingDirectory ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        assertTrue(PestParallelProgramRunner().canRun(PestParallelTestExecutor.EXECUTOR_ID, configuration))\n    }\n\n    fun testBuildFile() = doTest {\n        val configuration = createConfiguration(myFixture.file)\n\n        val pestParallelCommandSettings = createPestParallelCommand(configuration)\n\n        val expected = \"randomPath --teamcity --parallel --log-teamcity php://stdout /src/ATest.php\"\n        assertEquals(expected, pestParallelCommandSettings.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    fun testBuildFunction() = doTest {\n        val testElement = myFixture.file?.firstChild?.lastChild?.firstChild ?: return@doTest\n        val configuration = createConfiguration(testElement)\n\n        val pestParallelCommandSettings = createPestParallelCommand(configuration)\n\n        val expected = \"randomPath --teamcity --parallel --log-teamcity php://stdout /src/ATest.php\"\n        assertEquals(\n            expected,\n            pestParallelCommandSettings.createGeneralCommandLine().parametersList.list\n                .joinToString(\" \")\n                .substringBefore(\" --filter\")\n        )\n    }\n\n    fun testBuildDirectory() = doTest {\n        val testElement = myFixture.file?.containingDirectory ?: return@doTest\n        val configuration = createConfiguration(testElement)\n        val pestParallelCommandSettings = createPestParallelCommand(configuration)\n\n        val expected = \"randomPath --teamcity --parallel --log-teamcity php://stdout /src\"\n        assertEquals(expected, pestParallelCommandSettings.createGeneralCommandLine().parametersList.list.joinToString(\" \"))\n    }\n\n    private fun createConfiguration(psiElement: PsiElement): PestRunConfiguration {\n        createPestFrameworkConfiguration()\n        val context = ConfigurationContext.createEmptyContextForLocation(PsiLocation.fromPsiElement(psiElement))\n        val runConfiguration = PestRunConfigurationProducer().createConfigurationFromContext(context)?.configuration as? PestRunConfiguration\n        runConfiguration!!.settings.commandLineSettings.interpreterSettings.interpreterName = getTestName(false)\n        return runConfiguration\n    }\n\n    private fun doTest(block: () -> Unit) {\n        myFixture.configureByFile(\"ATest.php\")\n        block()\n    }\n\n    override fun setUp() {\n        super.setUp()\n        val interpreter = PhpInterpreter().apply {\n            name = getTestName(false)\n            homePath = \"$testDataPath/php\"\n        }\n        PhpInterpretersManagerImpl.getInstance(project).addInterpreter(interpreter)\n        configurationsBackup = PhpTestFrameworkSettingsManager.getInstance(project).getConfigurations(PestFrameworkType.instance)\n    }\n\n    override fun tearDown() {\n        try {\n            PhpTestFrameworkSettingsManager.getInstance(project).setConfigurations(PestFrameworkType.instance, configurationsBackup)\n            PhpInterpretersManagerImpl.getInstance(project).interpreters = emptyList()\n        } catch (e: Throwable) {\n            addSuppressedException(e)\n        } finally {\n            super.tearDown()\n        }\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/parallel/PestParallelSMTEventsAdapterTest.kt",
    "content": "package com.pestphp.pest.features.parallel\n\nimport com.intellij.execution.PsiLocation\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.process.ProcessOutputTypes\nimport com.intellij.execution.testframework.sm.runner.GeneralToSMTRunnerEventsConvertor\nimport com.intellij.execution.testframework.sm.runner.OutputToGeneralTestEventsConverter\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerEventsListener\nimport com.intellij.execution.testframework.sm.runner.SMTestProxy.SMRootTestProxy\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.psi.PsiElement\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.configuration.PestRunConfiguration\nimport com.pestphp.pest.configuration.PestRunConfigurationProducer\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/features/parallel\")\nclass PestParallelSMTEventsAdapterTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/features/parallel\"\n\n    fun testSuitePresentableName() {\n        val testsRoot = SMRootTestProxy()\n        processTestOutput(\n            testsRoot, \"\"\"\n##teamcity[testCount count='1' flowId='6630']\n\n##teamcity[testSuiteStarted name='P\\ATest' locationHint='php_qn:///Users/me/my_project/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()|'d code::\\P\\ATest' flowId='6630']\n\n##teamcity[testStarted name='__pest_evaluable_foo' locationHint='php_qn:///Users/me/my_project/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()|'d code::\\P\\ATest::__pest_evaluable_foo' flowId='6630']\n\n##teamcity[testFailed name='__pest_evaluable_foo' message='Failed asserting that 1 is identical to 2.' details='/Users/me/my_project/tests/kek/MyThirdTest.php:4|n' duration='80' flowId='6630']\n\n##teamcity[testFinished name='__pest_evaluable_foo' duration='82' flowId='6630']\n\n##teamcity[testSuiteFinished name='P\\ATest' flowId='6630']\"\"\".trimIndent()\n        )\n        val suite = testsRoot.children.first()\n        assertEquals(\"P\\\\ATest\", suite.name)\n        assertEquals(\"ATest\", suite.presentableName)\n    }\n\n    fun testTestPresentableName() {\n        val testsRoot = SMRootTestProxy()\n        processTestOutput(\n            testsRoot, \"\"\"\n##teamcity[testCount count='1' flowId='6630']\n\n##teamcity[testSuiteStarted name='P\\ATest' locationHint='php_qn:///Users/me/my_project/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()|'d code::\\P\\ATest' flowId='6630']\n\n##teamcity[testStarted name='__pest_evaluable_foo' locationHint='php_qn:///Users/me/my_project/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()|'d code::\\P\\ATest::__pest_evaluable_foo' flowId='6630']\n\n##teamcity[testFailed name='__pest_evaluable_foo' message='Failed asserting that 1 is identical to 2.' details='/Users/me/my_project/tests/kek/MyThirdTest.php:4|n' duration='80' flowId='6630']\n\n##teamcity[testFinished name='__pest_evaluable_foo' duration='82' flowId='6630']\n\n##teamcity[testSuiteFinished name='P\\ATest' flowId='6630']\"\"\".trimIndent()\n        )\n        val test = testsRoot.children.first().children.first()\n        assertEquals(\"__pest_evaluable_foo\", test.name)\n        assertEquals(\"foo\", test.presentableName)\n    }\n\n    fun testArchTestPresentableName() {\n        val testsRoot = SMRootTestProxy()\n        processTestOutput(\n            testsRoot, \"\"\"\n##teamcity[testCount count='1' flowId='6630']\n\n##teamcity[testSuiteStarted name='P\\ATest' locationHint='php_qn:///Users/me/my_project/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()|'d code::\\P\\ATest' flowId='6630']\n\n##teamcity[testStarted name='__pest_evaluable_preset__→_php_' locationHint='php_qn:///Users/me/my_project/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()|'d code::\\P\\ATest::__pest_evaluable_preset__→_php_' flowId='6630']\n\n##teamcity[testFailed name='__pest_evaluable_preset__→_php_' message='Failed asserting that 1 is identical to 2.' details='/Users/me/my_project/tests/kek/MyThirdTest.php:4|n' duration='80' flowId='6630']\n\n##teamcity[testFinished name='__pest_evaluable_preset__→_php_' duration='82' flowId='6630']\n\n##teamcity[testSuiteFinished name='P\\ATest' flowId='6630']\"\"\".trimIndent()\n        )\n        val test = testsRoot.children.first().children.first()\n        assertEquals(\"__pest_evaluable_preset__→_php_\", test.name)\n        assertEquals(\"preset → php\", test.presentableName)\n    }\n\n    private fun processTestOutput(testsRoot: SMRootTestProxy, output: String) {\n        val file = myFixture.configureByFile(\"ATest.php\")\n        project.messageBus.connect(testRootDisposable).subscribe(SMTRunnerEventsListener.TEST_STATUS, PestParallelSMTEventsAdapter())\n\n        createPestFrameworkConfiguration()\n        val context = ConfigurationContext.createEmptyContextForLocation(PsiLocation.fromPsiElement<PsiElement>(file))\n        val configuration = PestRunConfigurationProducer().createConfigurationFromContext(context)?.configuration as? PestRunConfiguration\n        val consoleProperties = configuration?.createTestConsoleProperties(PestParallelTestExecutor()) ?: return\n        disposeOnTearDown(consoleProperties)\n        val converter = OutputToGeneralTestEventsConverter(\"Pest\", consoleProperties)\n        converter.setProcessor(GeneralToSMTRunnerEventsConvertor(project, testsRoot, \"Pest\"))\n        StringUtil.splitByLinesKeepSeparators(output).forEach { line -> converter.process(line, ProcessOutputTypes.STDOUT) }\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/snapshotTesting/SnapshotLineMarkerProviderTest.kt",
    "content": "package com.pestphp.pest.features.snapshotTesting\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass SnapshotLineMarkerProviderTest: PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/snapshotTesting\"\n\n    fun testCanProvideIconForSnapshotAssertion() {\n        val file = myFixture.configureByFile(\"allSnapshotAssertions.php\")\n\n        val identifiers = file.firstChild.children\n            .map { it.firstChild.firstChild }\n        assertNotEmpty(identifiers)\n\n        assertSize(identifiers.count(), this.myFixture.findAllGutters())\n    }\n\n    fun testDoNotShowIconForSnapshotUse() {\n        myFixture.configureByFile(\"snapshotAssertionUseStatement.php\")\n\n        assertSize(0, this.myFixture.findAllGutters())\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/features/snapshotTesting/SnapshotUtilTest.kt",
    "content": "package com.pestphp.pest.features.snapshotTesting\n\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionImpl\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.PestLightCodeFixture\nimport junit.framework.TestCase\n\nclass SnapshotUtilTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/snapshotTesting\"\n\n    fun testIsSnapshotAssertion() {\n        val file = myFixture.configureByFile(\"allSnapshotAssertions.php\")\n\n        val statements = file.firstChild.children\n            .map { it.firstChild }\n        assertNotEmpty(statements)\n\n        statements.forEach { assertInstanceOf(it, FunctionReferenceImpl::class.java) }\n\n        statements\n            .filterIsInstance<FunctionReferenceImpl>()\n            .forEach {\n                TestCase.assertTrue(it.isSnapshotAssertionCall)\n            }\n    }\n\n    fun testIsNotSnapshotAssertion() {\n        val file = myFixture.configureByFile(\"nonSnapshotAssertions.php\")\n\n        val functionReference = file.firstChild.children[0].firstChild as FunctionReferenceImpl\n\n        assertFalse(functionReference.isSnapshotAssertionCall)\n    }\n\n    fun testCanFindSnapshotFiles() {\n        myFixture.copyFileToProject(\n            \"tests/__snapshots__/snapshotTest__it_renders_correctly__1.txt\",\n            \"tests/__snapshots__/snapshotTest__it_renders_correctly__1.txt\"\n        )\n\n        val file = myFixture.configureByFile(\"snapshotTest.php\")\n\n        val pestTest = file.firstChild.children[0].firstChild as FunctionReferenceImpl\n\n        val pestTestBody = pestTest.parameters[1].firstChild as FunctionImpl\n\n        val snapshotReference = pestTestBody.children[1].children[1].firstChild as FunctionReferenceImpl\n\n        assertSize(1, snapshotReference.snapshotFiles)\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/generateTest/PestNewTestFromClassActionTest.kt",
    "content": "package com.pestphp.pest.generateTest\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.ex.temp.TempFileSystem\nimport com.intellij.psi.PsiFile\nimport com.intellij.testFramework.PlatformTestUtil\nimport com.intellij.testFramework.utils.vfs.getPsiFile\nimport com.intellij.util.PathUtil\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.jetbrains.php.lang.psi.elements.Method\nimport com.jetbrains.php.phpunit.codeGeneration.PhpNewTestDialog\nimport com.jetbrains.php.roots.ui.PhpPsrDirectoryComboBox\nimport com.jetbrains.php.templates.PhpCreateFileFromTemplateDataProvider\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.PestNewTestFromClassAction\nimport com.pestphp.pest.PestTestCreateInfo\n\nclass PestNewTestFromClassActionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/generateTest\"\n\n    private fun createTestFile(file: PsiFile, namespace: String): PsiFile {\n        val dialog = getDialog(file, namespace)\n        try {\n            val testFile = MockPestNewTestFromClassAction.publicCreateFile(project, dialog)\n            assertNotNull(testFile)\n            return testFile!!\n        } finally {\n            dialog.disposeIfNeeded()\n        }\n    }\n\n    private object MockPestNewTestFromClassAction : PestNewTestFromClassAction() {\n        fun publicCreateFile(project: Project, dataProvider: PhpCreateFileFromTemplateDataProvider): PsiFile? {\n            return super.createFile(project, dataProvider)\n        }\n    }\n\n    private fun getDialog(file: PsiFile, namespace: String): PhpNewTestDialog {\n        val phpClass = PhpPsiUtil.findClass(file as PhpFile) { _ -> true }\n        return object : PhpNewTestDialog(project, file.containingDirectory, file, PestTestCreateInfo, phpClass) {\n            override fun createDirectoryCombobox(): PhpPsrDirectoryComboBox {\n                return object : PhpPsrDirectoryComboBox(project, testDirectoryProvider) {\n                    override fun init(baseDir: VirtualFile, namespace: String) {\n                        super.init(baseDir, namespace)\n                        updateSuggestions(getNamespace())\n                    }\n\n                    override fun getExistingParent(): VirtualFile {\n                        return findExistingParent(selectedPath) ?: PlatformTestUtil.getOrCreateProjectBaseDir(project)\n                    }\n\n                    private fun findExistingParent(path: String): VirtualFile? {\n                        if (StringUtil.isEmpty(path)) {\n                            return null\n                        }\n                        val directory = TempFileSystem.getInstance().findFileByPath(path)\n                        return directory ?: findExistingParent(PathUtil.getParentPath(path))\n                    }\n\n                    override fun getBaseDirectory(): VirtualFile {\n                        return myDirectoryCombobox.existingParent ?: super.getBaseDirectory()\n                    }\n                }\n            }\n\n            override fun getNamespace(): String {\n                return namespace\n            }\n\n            override fun getSelectedClassMembers(): MutableSet<Method> {\n                return phpClass?.methods?.toMutableSet() ?: mutableSetOf()\n            }\n        }\n    }\n\n    fun testPestWithNamespace() {\n        val fileWithClass = myFixture.copyFileToProject(\"testWithNamespace.php\")\n        val testFile = createTestFile(fileWithClass.getPsiFile(project), \"a\")\n        myFixture.configureByText(testFile.getName(), testFile.getText())\n        myFixture.checkResultByFile(\"testWithNamespace.after.php\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/goto/PestTestFinderTest.kt",
    "content": "package com.pestphp.pest.goto\n\nimport com.intellij.testFramework.UsefulTestCase\nimport com.jetbrains.php.lang.psi.PhpFile\nimport com.jetbrains.php.lang.psi.PhpPsiUtil\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.getPestTestName\nimport junit.framework.TestCase\n\nclass PestTestFinderTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/goto/PestTestFinder\"\n\n    fun testPestTestIsTest() {\n        val file = myFixture.configureByFile(\"test/App/UserTest.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertTrue(PestTestFinder().isTest(testElement))\n    }\n\n    fun testFileIsTest() {\n        val file = myFixture.configureByFile(\"test/App/UserTest.php\")\n\n        assertTrue(PestTestFinder().isTest(file))\n    }\n\n    fun testRandomElementIsTest() {\n        val file = myFixture.configureByFile(\"test/App/UserTest.php\")\n\n        assertTrue(PestTestFinder().isTest(file.firstChild.children.random()))\n    }\n\n    fun testClassIsNotTest() {\n        val file = myFixture.configureByFile(\"test/App/MockTest.php\")\n        myFixture.testDataPath\n\n        assertFalse(PestTestFinder().isTest(file.firstChild.lastChild.firstChild))\n    }\n\n    fun testCanFindSourceElement() {\n        val file = myFixture.configureByFile(\"App/User.php\")\n\n        TestCase.assertSame(\n            file,\n            PestTestFinder().findSourceElement(\n                PhpPsiUtil.findAllClasses(file as PhpFile).first().methods.first()\n            )\n        )\n    }\n\n    fun testFindTestFileForClass() {\n        val file = myFixture.configureByFile(\"App/User.php\")\n        val testFile = myFixture.configureByFile(\"test/App/UserTest.php\")\n\n        TestCase.assertSame(\n            testFile,\n            PestTestFinder().findTestsForClass(PhpPsiUtil.findAllClasses(file as PhpFile).first()).first(),\n        )\n    }\n\n    fun testFindClassForTestFile() {\n        val file = myFixture.configureByFile(\"App/User.php\")\n        val testFile = myFixture.configureByFile(\"test/App/UserTest.php\")\n\n        TestCase.assertSame(\n            PhpPsiUtil.findAllClasses(file as PhpFile).first(),\n            PestTestFinder().findClassesForTest(testFile.firstChild).first()\n        )\n    }\n\n    fun testFindTestsForMethod() {\n        val file = myFixture.configureByFile(\"App/User.php\")\n        val testFile = myFixture.configureByFile(\"test/App/UserTest.php\")\n        val method = PhpPsiUtil.findAllClasses(file as PhpFile).first().findMethodByName(\"isPestDeveloper\")\n        val tests = testFile.firstChild.children.map { it.firstChild }.filter {\n            it.getPestTestName()?.contains(\"is pest developer\") == true\n        }\n\n        UsefulTestCase.assertSameElements(\n            PestTestFinder().findTestsForClass(method!!),\n            tests\n        )\n    }\n\n    fun testFindMethodsForTest() {\n        val file = myFixture.configureByFile(\"App/User.php\")\n        val testFile = myFixture.configureByFile(\"test/App/UserTest.php\")\n        val test = testFile.firstChild.children.map { it.firstChild }.first {\n            it.getPestTestName() == \"is pest developer\"\n        }\n        val methods = PhpPsiUtil.findAllClasses(file as PhpFile).first().methods.filter { it.name.contains(\"is\") }\n\n        UsefulTestCase.assertSameElements(\n            PestTestFinder().findClassesForTest(test),\n            methods\n        )\n    }\n\n    fun testFindTestsForMethodInDescribeBlock() {\n        val file = myFixture.configureByFile(\"App/User.php\")\n        myFixture.configureByFile(\"test/App/UserDescribeTest.php\")\n        val method = PhpPsiUtil.findAllClasses(file as PhpFile).first().findMethodByName(\"isPestDeveloper\")\n\n        val tests = PestTestFinder().findTestsForClass(method!!)\n\n        assertTrue(tests.any { it.getPestTestName()?.contains(\"is pest developer in describe\") == true })\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/higherOrderExpectations/HigherOrderExpectationAssertionCompletionTest.kt",
    "content": "package com.pestphp.pest.higherOrderExpectations\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass HigherOrderExpectationAssertionCompletionTest: PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/higherOrderExpectations\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyFileToProject(\"stubs.php\")\n    }\n\n    fun testFieldCompletion() {\n        myFixture.configureByFile(\n            \"ExpectPropertyAssertionCompletion.php\"\n        )\n\n        assertCompletion(\"toBeTrue\", \"toEqual\")\n    }\n\n    fun testMethodCompletion() {\n        myFixture.configureByFile(\n            \"ExpectMethodAssertionCompletion.php\"\n        )\n\n        assertCompletion(\"toBeTrue\", \"toEqual\")\n    }\n\n    fun testMethodCompletionChained() {\n        myFixture.configureByFile(\n            \"ExpectMethodAssertionCompletionChained.php\"\n        )\n\n        assertCompletion(\"toBeInt\")\n    }\n\n    fun testFieldCompletionChained() {\n        myFixture.configureByFile(\n            \"ExpectPropertyAssertionCompletionChained.php\"\n        )\n\n        assertCompletion(\"toBeInt\")\n    }\n\n    fun testMethodCompletionChainedAssertions() {\n        myFixture.configureByFile(\n            \"ExpectMethodAssertionCompletionChainedAssertions.php\"\n        )\n\n        assertCompletion(\"toBeInt\")\n    }\n\n    fun testFieldCompletionChainedAssertions() {\n        myFixture.configureByFile(\n            \"ExpectPropertyAssertionCompletionChainedAssertions.php\"\n        )\n\n        assertCompletion(\"toBeInt\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/higherOrderExpectations/HigherOrderExpectationCompletionTest.kt",
    "content": "package com.pestphp.pest.higherOrderExpectations\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass HigherOrderExpectationCompletionTest: PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/higherOrderExpectations\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyFileToProject(\".phpstorm.meta.php\")\n    }\n\n    fun testFieldCompletion() {\n        myFixture.configureByFile(\n            \"ExpectPropertyCompletion.php\"\n        )\n\n        assertCompletion(\"otherExample\", \"test\")\n    }\n\n    fun testMethodCompletion() {\n        myFixture.configureByFile(\n            \"ExpectMethodCompletion.php\"\n        )\n\n        assertCompletion(\"getOtherExample\", \"getTest\")\n    }\n\n    fun testMethodCompletionChained() {\n        myFixture.configureByFile(\n            \"ExpectMethodCompletionChained.php\"\n        )\n\n        assertCompletion(\"getTimestamp\");\n    }\n\n    fun testFieldCompletionChained() {\n        myFixture.configureByFile(\n            \"ExpectPropertyCompletionChained.php\"\n        )\n\n        assertCompletion(\"getTimestamp\")\n    }\n\n    fun testChainedAssertionsFieldCompletion() {\n        myFixture.configureByFile(\n            \"ExpectPropertyCompletionChainedAssertions.php\"\n        )\n\n        assertCompletion(\"otherExample\", \"test\")\n    }\n\n    fun testChainedAssertionsMethodCompletion() {\n        myFixture.configureByFile(\n            \"ExpectMethodCompletionChainedAssertions.php\"\n        )\n\n        assertCompletion(\"getOtherExample\", \"getTest\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/indexers/PestTestIndexTest.kt",
    "content": "package com.pestphp.pest.indexers\n\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.testFramework.TestDataPath\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/indexers/PestTestIndexTest\")\nclass PestTestIndexTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/indexers/PestTestIndexTest\"\n\n    fun testPestTestFileIsIndexed() {\n        val virtualFile = myFixture.copyFileToProject(\"FileWithPestTest.php\", \"tests/FileWithPestTest.php\")\n        myFixture.configureFromExistingVirtualFile(virtualFile)\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val indexKeys = fileBasedIndex.getAllKeys(key, project).filter {\n            fileBasedIndex.getContainingFiles(key, it, GlobalSearchScope.allScope(project)).isNotEmpty()\n        }\n\n        assertContainsElements(indexKeys, \"/src/tests/FileWithPestTest.php\")\n    }\n\n    fun testPhpFileIsNotIndexed() {\n        myFixture.copyFileToProject(\"FileWithoutPestTest.php\", \"tests/FileWithoutPestTest.php\")\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val indexKeys = fileBasedIndex.getAllKeys(key, project).filter {\n            fileBasedIndex.getContainingFiles(key, it, GlobalSearchScope.allScope(project)).isNotEmpty()\n        }\n\n        assertDoesntContain(indexKeys, \"/src/tests/FileWithoutPestTest.php\")\n    }\n\n    fun testPestTestFileWithTodoIsIndexed() {\n        val virtualFile = myFixture.copyFileToProject(\"FileWithPestTodosTest.php\", \"tests/FileWithPestTodosTest.php\")\n        myFixture.configureFromExistingVirtualFile(virtualFile)\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val indexKeys = fileBasedIndex.getAllKeys(key, project).filter {\n            fileBasedIndex.getContainingFiles(key, it, GlobalSearchScope.allScope(project)).isNotEmpty()\n        }\n\n        assertContainsElements(indexKeys, \"/src/tests/FileWithPestTodosTest.php\")\n    }\n\n    fun testPestTestFileOutsideTestDirectoryIsIndexed() {\n        val virtualFile = myFixture.copyFileToProject(\"FileWithPestTest.php\", \"anywhere-but-not-te-st/FileWithPestTest.php\")\n        myFixture.configureFromExistingVirtualFile(virtualFile)\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val indexKeys = fileBasedIndex.getAllKeys(key, project).filter {\n            fileBasedIndex.getContainingFiles(key, it, GlobalSearchScope.allScope(project)).isNotEmpty()\n        }\n\n        assertContainsElements(indexKeys, \"/src/anywhere-but-not-te-st/FileWithPestTest.php\")\n    }\n\n    fun testDescribeBlockTestsAreIndexed() {\n        val virtualFile = myFixture.copyFileToProject(\"FileWithDescribeBlockTest.php\", \"tests/FileWithDescribeBlockTest.php\")\n        myFixture.configureFromExistingVirtualFile(virtualFile)\n\n        val fileBasedIndex = FileBasedIndex.getInstance()\n\n        val testNames = fileBasedIndex.getValues(key, \"/src/tests/FileWithDescribeBlockTest.php\", GlobalSearchScope.allScope(project))\n            .flatten()\n\n        assertContainsElements(testNames, \"`sum` → \", \"`sum` → adds numbers\", \"`sum` → it handles zero\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/inspections/InvalidTestNameCaseInspectionTest.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/inspections\")\nclass InvalidTestNameCaseInspectionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/inspections\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.enableInspections(InvalidTestNameCaseInspection::class.java)\n    }\n\n    fun testInvalidTestNameCase() {\n        myFixture.configureByFile(\"InvalidTestNameCase.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testValidTestNameCase() {\n        myFixture.configureByFile(\"ValidTestNameCase.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testReplacesInvalidTestNameCase() {\n        myFixture.configureByFile(\"InvalidTestNameCase.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n        myFixture.checkResultByFile(\"InvalidTestNameCase.after.php\")\n    }\n\n    fun testValidTestNameWhenWrongCasingOnSomeWords() {\n        myFixture.configureByFile(\"ValidTestNameWhenWrongCasingOnOneWord.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testValidTestNameCaseInHigherOrderTest() {\n        myFixture.configureByFile(\"ValidHigherOrderTestNameCase.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testValidItTestNameWithoutSpaces() {\n        myFixture.configureByFile(\"ValidItTestNameWithoutSpaces.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testInvalidTestNameAndDatasetName() {\n        myFixture.configureByFile(\"InvalidTestNameAndDatasetName.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n        myFixture.checkResultByFile(\"InvalidTestNameAndDatasetName.after.php\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/inspections/MissingScreenshotSnapshotInspectionTest.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/inspections\")\nclass MissingScreenshotSnapshotInspectionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/inspections\"\n\n    override fun setUp() {\n        super.setUp()\n        myFixture.enableInspections(MissingScreenshotSnapshotInspection::class.java)\n    }\n\n    fun testSimple() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/MissingScreenshotSnapshot.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testSimpleExists() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/ScreenshotSnapshot.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testMultiple() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/MissingScreenshotSnapshotMultiple.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testMultipleExists() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/ScreenshotSnapshotMultiple.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testComplexName() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/MissingScreenshotSnapshotComplexName.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testComplexNameExists() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/ScreenshotSnapshotComplexName.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testNestedDir() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/nested/MissingScreenshotSnapshotNested.php\")\n        myFixture.checkHighlighting()\n    }\n\n    fun testNestedDirExists() {\n        myFixture.copyDirectoryToProject(\"screenshotProject\", \".\")\n        myFixture.configureFromTempProjectFile(\"tests/Feature/nested/ScreenshotSnapshotNested.php\")\n        myFixture.checkHighlighting()\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/inspections/MultipleExpectChainableInspectionTest.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/inspections\")\nclass MultipleExpectChainableInspectionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/inspections\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.enableInspections(MultipleExpectChainableInspection::class.java)\n    }\n\n    fun testHasMultipleExpectCall() {\n        myFixture.configureByFile(\"MultipleExpectCall.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testSingleExpectCall() {\n        myFixture.configureByFile(\"SingleExpectCall.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testReplacesMultipleExpectCallToChain() {\n        myFixture.configureByFile(\"MultipleExpectCall.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().first().run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"MultipleExpectCall.after.php\")\n    }\n\n    fun testHasExpectCallsWithOtherStatementsBetween() {\n        myFixture.configureByFile(\"ExpectCallsWithOtherStatementsBetween.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testHasMultipleExpectCallsWithOtherStatementsBetween() {\n        myFixture.configureByFile(\"MultipleExpectCallsWithOtherStatementsBetween.php\")\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testReplaceMultipleExpectCalls() {\n        myFixture.configureByFile(\"MultipleExpectCallsWithOtherStatementsBetween.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().first().run { myFixture.launchAction(this) }\n        myFixture.getAllQuickFixes().last().run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"MultipleExpectCallsWithOtherStatementsBetween.after.php\")\n    }\n\n    fun testReplaceCanBeCalledOnFirstStatement() {\n        myFixture.configureByFile(\"MultipleExpectCall.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().first().run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"MultipleExpectCall.after.php\")\n    }\n\n    fun testReplaceCanBeCalledOnLastStatement() {\n        myFixture.configureByFile(\"MultipleExpectCall.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes().last().run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"MultipleExpectCall.after.php\")\n    }\n\n    fun testReplaceCanBeCalledOnSecondaryStatement() {\n        myFixture.configureByFile(\"ManyExpectCall.php\")\n\n        myFixture.checkHighlighting()\n        myFixture.getAllQuickFixes()[3].run { myFixture.launchAction(this) }\n\n        myFixture.checkResultByFile(\"ManyExpectCall.after.php\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/inspections/PestAssertionCanBeSimplifiedInspectionTest.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestAssertionCanBeSimplifiedInspectionTest: PestLightCodeFixture() {\n  override fun getBasePath(): String = \"${super.getBasePath()}/inspections/assertionCanBeSimplified\"\n\n  override fun setUp() {\n    super.setUp()\n\n    myFixture.enableInspections(PestAssertionCanBeSimplifiedInspection::class.java)\n  }\n\n  fun testToBeWithTrue() {\n    myFixture.configureByFile(\"ToBeWithTrue.php\")\n\n    myFixture.checkHighlighting()\n    myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n    myFixture.checkResultByFile(\"ToBeWithTrue.after.php\")\n  }\n\n  fun testToBeWithFalse() {\n    myFixture.configureByFile(\"ToBeWithFalse.php\")\n\n    myFixture.checkHighlighting()\n    myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n    myFixture.checkResultByFile(\"ToBeWithFalse.after.php\")\n  }\n\n  fun testToBeWithNull() {\n    myFixture.configureByFile(\"ToBeWithNull.php\")\n\n    myFixture.checkHighlighting()\n    myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n    myFixture.checkResultByFile(\"ToBeWithNull.after.php\")\n  }\n\n  fun testToHaveCountWithZero() {\n    myFixture.configureByFile(\"ToHaveCountWithZero.php\")\n\n    myFixture.checkHighlighting()\n    myFixture.getAllQuickFixes().forEach { myFixture.launchAction(it) }\n\n    myFixture.checkResultByFile(\"ToHaveCountWithZero.after.php\")\n  }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/inspections/PestTestFailedLineInspectionTest.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.execution.TestStateStorage\nimport com.intellij.lang.annotation.HighlightSeverity\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\nimport java.util.Date\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/inspections/pestTestFailedLine\")\nclass PestTestFailedLineInspectionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/inspections/pestTestFailedLine\"\n\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.enableInspections(PestTestFailedLineInspection::class.java)\n    }\n\n    private fun writeTestState(record: TestStateStorage.Record) {\n        TestStateStorage.getInstance(project).writeState(\"pest_qn://src/${getFileNameBeforeRelativePath()}::myTest\", record)\n    }\n\n    private fun writeTestStateWithDataset(record: TestStateStorage.Record, dataset: String) {\n        TestStateStorage.getInstance(project).writeState(\"pest_qn://src/${getFileNameBeforeRelativePath()}::myTest with data set $dataset\", record)\n    }\n\n    private fun doTest(record: TestStateStorage.Record) {\n        writeTestState(record)\n        configureByFile()\n        myFixture.checkHighlighting()\n    }\n\n    fun testFailedOneLine() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 5,\n            \"    expect(1)->toBe(2);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testMismatchLine() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 5,\n            \"    expect(1)->toBe(3);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testOutRange() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 12,\n            \"    expect(1)->toBe(2);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testTypeBefore() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 5,\n            \"    expect(1)->toBe(2);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n        myFixture.type(\"\\n echo 1;\")\n        val highlightInfos = myFixture.doHighlighting(HighlightSeverity.WARNING)\n        assertEquals(1, highlightInfos.size)\n        assertEquals(76, highlightInfos[0].startOffset)\n        assertEquals(95, highlightInfos[0].endOffset)\n    }\n\n    fun testTypeInside() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 5,\n            \"    expect(1)->toBe(2);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n        myFixture.type(\"Not\")\n        val highlightInfos = myFixture.doHighlighting(HighlightSeverity.WARNING)\n        assertEquals(1, highlightInfos.size)\n        assertEquals(63, highlightInfos[0].startOffset)\n        assertEquals(85, highlightInfos[0].endOffset)\n    }\n\n    fun testAnonymousFunction() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 7,\n            \"    });\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testLambdaFunction() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 6,\n            \"    );\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testSingleLeafElementReported() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 8,\n            \"    );\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testMultipleStatementsInOneLine() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(1); expect(1)->toBe(2);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        doTest(record)\n    }\n\n    fun testWithDataSetAndKeys() {\n        val secondDatasetRecord = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        writeTestStateWithDataset(secondDatasetRecord, \"\\\"dataset \\\"\\\"second dataset\\\"\")\n        val thirdDatasetRecord = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 3.\", null\n        )\n        writeTestStateWithDataset(thirdDatasetRecord, \"\\\"dataset \\\"\\\"third dataset\\\"\")\n        val methodRecord = TestStateStorage.Record(6, Date(), 0, -1, \"\", \"\", null)\n        doTest(methodRecord)\n    }\n\n    fun testWithDataSet() {\n        val secondDatasetRecord = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        writeTestStateWithDataset(secondDatasetRecord, \"(2)\")\n        val thirdDatasetRecord = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 3.\", null\n        )\n        writeTestStateWithDataset(thirdDatasetRecord, \"(3)\")\n        val methodRecord = TestStateStorage.Record(6, Date(), 0, -1, \"\", \"\", null)\n        doTest(methodRecord)\n    }\n\n    fun testWithNamedDataSet() {\n        val secondDatasetRecord = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        writeTestStateWithDataset(secondDatasetRecord, \"second dataset\")\n        val thirdDatasetRecord = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 3.\", null\n        )\n        writeTestStateWithDataset(thirdDatasetRecord, \"third dataset\")\n        val methodRecord = TestStateStorage.Record(6, Date(), 0, -1, \"\", \"\", null)\n        doTest(methodRecord)\n    }\n\n    fun testWithDataSetAndSeveralFails() {\n        val record = TestStateStorage.Record(\n            6, Date(), 0, 4,\n            \"    expect(1)->toBe(\\$a);\",\n            \"Failed asserting that 1 is identical to 2.\", null\n        )\n        writeTestStateWithDataset(record, \"\\\"dataset \\\"\\\"second dataset\\\"\")\n\n        val record2 = TestStateStorage.Record(\n            6, Date(), 0, 5,\n            \"    expect(2)->toBe(\\$a);\",\n            \"Failed asserting that 2 is identical to 1.\", null\n        )\n        writeTestStateWithDataset(record2, \"\\\"dataset \\\"\\\"first dataset\\\"\")\n        val methodRecord = TestStateStorage.Record(6, Date(), 0, -1, \"\", \"\", null)\n        doTest(methodRecord)\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/inspections/PhpStormInspectionsTest.kt",
    "content": "package com.pestphp.pest.inspections\n\nimport com.intellij.testFramework.PsiTestUtil\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.lang.inspections.PhpInspection\nimport com.jetbrains.php.lang.inspections.psr0.PhpMultipleClassesDeclarationsInOneFile\nimport com.pestphp.pest.PestLightCodeFixture\nimport org.jetbrains.jps.model.java.JavaSourceRootType\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/inspections/phpstorm\")\nclass PhpStormInspectionsTest: PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/inspections/phpstorm\"\n\n    private fun doTest(inspectionClass: Class<out PhpInspection>, testFilePath: String) {\n        myFixture.enableInspections(inspectionClass)\n\n        myFixture.configureByFile(testFilePath)\n\n        myFixture.checkHighlighting()\n    }\n\n    fun testMultipleClassesDeclarationsInPestFile() {\n        val testSrcDir = myFixture.tempDirFixture.findOrCreateDir(\"tests\")\n        PsiTestUtil.addSourceRoot(module, testSrcDir, JavaSourceRootType.TEST_SOURCE)\n        myFixture.copyFileToProject(\"MultipleClassesDeclarationsInPestFileTest.php\", \"${testSrcDir.name}/MultipleClassesDeclarationsInPestFileTest.php\")\n        doTest(PhpMultipleClassesDeclarationsInOneFile::class.java, \"${testSrcDir.name}/MultipleClassesDeclarationsInPestFileTest.php\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/runner/PestPressToContinueActionTest.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.executors.DefaultRunExecutor\nimport com.intellij.execution.impl.ConsoleViewImpl\nimport com.intellij.execution.process.NopProcessHandler\nimport com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView\nimport io.mockk.every\nimport io.mockk.mockk\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.DataContext\nimport com.intellij.openapi.actionSystem.LangDataKeys\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.testFramework.TestActionEvent.createTestEvent\nimport com.intellij.util.ui.UIUtil\nimport com.jetbrains.php.util.pathmapper.PhpPathMapper\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.configuration.PestLocationProvider\nimport com.pestphp.pest.configuration.PestRunConfigurationType\nimport java.io.ByteArrayOutputStream\nimport java.io.OutputStream\n\nclass PestPressToContinueActionTest : PestLightCodeFixture() {\n\n    private class CapturingProcessHandler : NopProcessHandler() {\n        val input = ByteArrayOutputStream()\n        override fun getProcessInput(): OutputStream = input\n    }\n\n    private data class TestContext(\n        val consoleView: SMTRunnerConsoleView,\n        val processHandler: CapturingProcessHandler,\n        val action: PestPressToContinueAction,\n        val event: AnActionEvent,\n    )\n\n    private fun setupWithPrinted(output: String): TestContext {\n        val config = mockk<RunConfiguration>()\n        every { config.project } returns project\n        every { config.name } returns \"Test\"\n        val executor = DefaultRunExecutor.getRunExecutorInstance()\n        val locator = PestLocationProvider(PhpPathMapper.create(project), project, myFixture.testDataPath)\n        val props = PestConsoleProperties(config, executor, locator)\n\n        val consoleView = SMTRunnerConsoleView(props)\n        consoleView.initUI()\n        Disposer.register(testRootDisposable, consoleView)\n\n        val processHandler = CapturingProcessHandler()\n        processHandler.startNotify()\n        consoleView.attachToProcess(processHandler)\n\n        val descriptor = RunContentDescriptor(consoleView, processHandler, consoleView.component, \"Pest\")\n        descriptor.runConfigurationTypeId = PestRunConfigurationType.instance.id\n        Disposer.register(testRootDisposable, descriptor)\n\n        val innerConsole = (consoleView.console as ConsoleViewImpl)\n        innerConsole.print(output, ConsoleViewContentType.NORMAL_OUTPUT)\n        innerConsole.flushDeferredText()\n        UIUtil.dispatchAllInvocationEvents()\n\n        val action = PestPressToContinueAction()\n        val dataContext = DataContext { dataId ->\n            if (LangDataKeys.RUN_CONTENT_DESCRIPTOR.name == dataId) descriptor else null\n        }\n        val event = createTestEvent(action, dataContext)\n        action.update(event)\n\n        return TestContext(consoleView, processHandler, action, event)\n    }\n\n    fun testWritesNewlineWhenPromptPresent() {\n        val ctx = setupWithPrinted(\"Press any key to continue...\")\n        assertTrue(ctx.event.presentation.isVisible)\n        assertTrue(ctx.event.presentation.isEnabled)\n\n        ctx.action.actionPerformed(ctx.event)\n\n        assertEquals(\"\\n\", ctx.processHandler.input.toString(Charsets.UTF_8))\n    }\n\n    fun testDisabledWhenPromptAbsent() {\n        val ctx = setupWithPrinted(\"no prompt here\")\n        assertTrue(ctx.event.presentation.isVisible)\n        assertFalse(ctx.event.presentation.isEnabled)\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/runner/PestTestStackTraceParserTest.kt",
    "content": "package com.pestphp.pest.runner\n\nimport com.intellij.testFramework.TestDataPath\nimport com.intellij.util.PathMappingSettings\nimport com.jetbrains.php.util.pathmapper.PhpPathMapper\nimport com.jetbrains.php.util.pathmapper.PhpRemotePathMapper\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.configuration.PestLocationProvider\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/runner/pestTestStacktraceParser\")\nclass PestTestStackTraceParserTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/runner/pestTestStacktraceParser\"\n\n    private fun createStackTraceParser(url: String, stacktrace: String?, message: String?): PestTestStackTraceParser {\n        val locator = PestLocationProvider(PhpPathMapper.create(project), project, myFixture.testDataPath)\n        return parse(url, stacktrace, message, locator, project)\n    }\n\n    private fun createRemoteStackTraceParser(url: String, stacktrace: String?, message: String?): PestTestStackTraceParser {\n        val locator = PestLocationProvider(PhpRemotePathMapper.create(listOf(PathMappingSettings.PathMapping(getFileBeforeFullPath(), \"/opt/project\"))), project, myFixture.testDataPath)\n        return parse(url, stacktrace, message, locator, project)\n    }\n\n    fun testNull() {\n        val stackTraceParser = createStackTraceParser(\"pest_qn://path/to/test.php::myTest\", null, null)\n        assertNull(stackTraceParser.errorMessage)\n        assertEquals(-1, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertNull(stackTraceParser.failedMethodName)\n    }\n\n    fun testEmptyLine() {\n        val stackTraceParser = createStackTraceParser(\"pest_qn://path/to/test.php::myTest\", \"\", \"error\")\n        assertEquals(\"error\", stackTraceParser.errorMessage)\n        assertEquals(-1, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertNull(stackTraceParser.failedMethodName)\n    }\n\n    fun testWithoutTestMention() {\n        val stackTraceParser =\n            createStackTraceParser(\"pest_qn://path/to/test.php::myTest\", \"at /path/to/test2.php:14\\nat /path/to/test3.php:11\", \"error\")\n        assertEquals(\"error\", stackTraceParser.errorMessage)\n        assertEquals(-1, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertNull(stackTraceParser.failedMethodName)\n    }\n\n    fun testOneLine() {\n        configureByFile()\n        val stackTraceParser =\n            createStackTraceParser(\"pest_qn://\" + getFileBeforeFullPath() + \"::myTest\", getFileBeforeFullPath() + \":4\", \"error\")\n        assertEquals(\"error\", stackTraceParser.errorMessage)\n        assertEquals(4, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertEquals(\"   expect(1)->toBe(2);\", stackTraceParser.failedMethodName)\n    }\n\n    fun testMultiline() {\n        configureByFile()\n        val stackTraceParser = createStackTraceParser(\n            \"pest_qn://\" + getFileBeforeFullPath() + \"::\\\\MyTest::test\",\n            getFileBeforeFullPath() + \":12\\n \" +\n                getFileBeforeFullPath() + \":8\\n\" +\n                getFileBeforeFullPath() + \":4\", null\n        )\n        assertEquals(getFileBeforeFullPath() + \":12\", stackTraceParser.errorMessage)\n        assertEquals(4, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertEquals(\"    foo();\", stackTraceParser.failedMethodName)\n    }\n\n\n    fun testWrongLineNumber() {\n        configureByFile()\n        val stackTraceParser =\n            createStackTraceParser(\"pest_qn://\" + getFileBeforeFullPath() + \"::myTest\", getFileBeforeFullPath() + \":seven\", \"error\")\n        assertEquals(\"error\", stackTraceParser.errorMessage)\n        assertEquals(-1, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertNull(stackTraceParser.failedMethodName)\n    }\n\n    fun testOutRangeLineNumber() {\n        configureByFile()\n        val stackTraceParser =\n            createStackTraceParser(\"pest_qn://\" + getFileBeforeFullPath() + \"::myTest\", getFileBeforeFullPath() + \":10\", \"error\")\n        assertEquals(\"error\", stackTraceParser.errorMessage)\n        assertEquals(10, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertNull(stackTraceParser.failedMethodName)\n    }\n\n    fun testOneLineRemote() {\n        configureByFile()\n        val stackTraceParser =\n            createRemoteStackTraceParser(\"pest_qn://\" + getFileBeforeFullPath() + \"::myTest\", getFileBeforeFullPath() + \":4\", \"error\")\n        assertEquals(\"error\", stackTraceParser.errorMessage)\n        assertEquals(4, stackTraceParser.failedLine)\n        assertNull(stackTraceParser.topLocationLine)\n        assertEquals(\"   expect(1)->toBe(2);\", stackTraceParser.failedMethodName)\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/surrounders/ExpectStatementSurrounderTest.kt",
    "content": "package com.pestphp.pest.surrounders\n\nclass ExpectStatementSurrounderTest: SurroundTestCase() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/surrounders\"\n\n    fun testSurroundSingleElements() {\n        doTest(\n            ExpectStatementSurrounder(),\n            \"\"\"\n                <?php\n                \n                it('basic', function () {\n                    <selection>true</selection>\n                });\n            \"\"\".trimIndent(),\n            \"\"\"\n                <?php\n                \n                it('basic', function () {\n                    <selection>expect(true)</selection>\n                });\n            \"\"\".trimIndent()\n        )\n    }\n\n    fun testSurroundMultipleElements() {\n        doTest(\n            ExpectStatementSurrounder(),\n            \"\"\"\n                <?php\n                \n                it('basic', function () {\n                    <selection>100 === 200</selection>\n                });\n            \"\"\".trimIndent(),\n            \"\"\"\n                <?php\n                \n                it('basic', function () {\n                    <selection>expect(100 === 200)</selection>\n                });\n            \"\"\".trimIndent()\n        )\n    }\n\n    fun testNoSurroundOutsideTest() {\n        doTest(\n            ExpectStatementSurrounder(),\n            \"\"\"\n                <?php\n                \n                <selection>true</selection>\n            \"\"\".trimIndent(),\n            \"\"\"\n                <?php\n                \n                true\n            \"\"\".trimIndent()\n        )\n    }\n\n    fun testNoSurroundOnTestItself() {\n        doTest(\n            ExpectStatementSurrounder(),\n            \"\"\"\n                <?php\n                \n                <selection>it('basic', function () {\n                    expect(true)->toBeTrue();\n                });</selection>\n            \"\"\".trimIndent(),\n            \"\"\"\n                <?php\n                \n                it('basic', function () {\n                    expect(true)->toBeTrue();\n                });\n            \"\"\".trimIndent()\n        )\n    }\n\n    fun testSurroundInsideDescribeBlock() {\n        doTest(\n            ExpectStatementSurrounder(),\n            \"\"\"\n                <?php\n\n                describe('sum', function () {\n                    it('adds numbers', function () {\n                        <selection>1 + 1</selection>\n                    });\n                });\n            \"\"\".trimIndent(),\n            \"\"\"\n                <?php\n\n                describe('sum', function () {\n                    it('adds numbers', function () {\n                        <selection>expect(1 + 1)</selection>\n                    });\n                });\n            \"\"\".trimIndent()\n        )\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/surrounders/SurroundTestCase.kt",
    "content": "package com.pestphp.pest.surrounders\n\nimport com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\nimport com.intellij.lang.surroundWith.Surrounder\nimport com.pestphp.pest.PestLightCodeFixture\n\nabstract class SurroundTestCase: PestLightCodeFixture() {\n    protected fun doTest(surrounder: Surrounder, textBefore: String, textAfter: String) {\n        myFixture.configureByText(\"simpleTest.php\", textBefore)\n        SurroundWithHandler.invoke(project, myFixture.editor, myFixture.file, surrounder)\n        myFixture.checkResult(textAfter)\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/templates/PestPostfixTemplateProviderTest.kt",
    "content": "package com.pestphp.pest.templates\n\nimport com.pestphp.pest.PestLightCodeFixture\n\nclass PestPostfixTemplateProviderTest: PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/templates\"\n\n    private fun doTest() {\n        myFixture.configureByFile(getTestName(true) + \".php\")\n        myFixture.type('\\t')\n        myFixture.checkResultByFile(getTestName(true) + \".after.php\", true)\n    }\n\n    fun testIt() {\n        doTest()\n    }\n\n    fun testDescribe() {\n        doTest()\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/types/BaseTypeTestCase.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/types\")\nabstract class BaseTypeTestCase : PestLightCodeFixture() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyFileToProject(\"TestCase.php\")\n    }\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/types\"\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/types/ExpectCallCompletionTest.kt",
    "content": "package com.pestphp.pest.types\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.testFramework.replaceService\nimport com.jetbrains.php.composer.configData.ComposerConfigManager\nimport io.mockk.every\nimport io.mockk.mockk\nimport java.util.concurrent.TimeUnit\n\nclass ExpectCallCompletionTest : BaseTypeTestCase() {\n    override fun setUp() {\n        super.setUp()\n\n        val dir = myFixture.copyDirectoryToProject(\"expect\", \"tests\")\n        myFixture.addFileToProject(\"composer.json\", \"\")\n\n        val composerConfigManagerMock = mockk<ComposerConfigManager>(relaxUnitFun = true) {\n            every { getConfig(null as PsiElement?) } returns dir\n        }\n        project.replaceService(\n            ComposerConfigManager::class.java,\n            composerConfigManagerMock,\n            testRootDisposable\n        )\n    }\n\n    fun testFieldCompletions() {\n        myFixture.configureByFile(\"tests/expectCallCompletion.php\")\n\n        waitForAppLeakingThreads(10, TimeUnit.SECONDS)\n\n        assertCompletion(\"someExtend\")\n    }\n\n    fun testFieldCompletionsChainedNotProperty() {\n        myFixture.configureByFile(\"tests/expectCallCompletionChainedNotProperty.php\")\n\n        assertCompletion(\"someExtend\")\n    }\n\n    fun testFieldCompletionsChainedNotMethod() {\n        myFixture.configureByFile(\"tests/expectCallCompletionChainedNotMethod.php\")\n\n        assertCompletion(\"someExtend\")\n    }\n}"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/types/FunctionTypeTest.kt",
    "content": "package com.pestphp.pest.types\n\nclass FunctionTypeTest : BaseTypeTestCase() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyDirectoryToProject(\"function\", \"/\")\n    }\n\n    fun testTestFunction() {\n        myFixture.configureByFile(\"testTest.php\")\n        assertFunctionCompletion()\n    }\n\n    private fun assertFunctionCompletion() {\n        assertCompletion(\"expectException\", \"expectExceptionCode\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/types/ThisFieldCompletionTest.kt",
    "content": "package com.pestphp.pest.types\n\nclass ThisFieldCompletionTest : BaseTypeTestCase() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyDirectoryToProject(\"thisField\", \"tests\")\n    }\n\n    fun testFieldCompletions() {\n        myFixture.configureByFile(\"tests/beforeEachCompletion.php\")\n\n        assertCompletion(\"foo\")\n    }\n\n    fun testFieldCompletionWithNamespace() {\n        myFixture.configureByFile(\"tests/beforeEachNamespaceCompletion.php\")\n\n        assertCompletion(\"foo\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/types/ThisFieldTypeTest.kt",
    "content": "package com.pestphp.pest.types\n\nclass ThisFieldTypeTest : BaseTypeTestCase() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyDirectoryToProject(\"thisField\", \"tests\")\n    }\n\n    fun testBeforeEach() {\n        myFixture.configureByFile(\"tests/beforeEach.php\")\n\n        assertCompletion(\"a\", \"b\")\n    }\n\n    fun testBeforeEachNamespace() {\n        myFixture.configureByFile(\"tests/beforeEachNamespace.php\")\n\n        assertCompletion(\"a\", \"b\")\n    }\n\n    fun testAfterEach() {\n        myFixture.configureByFile(\"tests/afterEach.php\")\n\n        assertCompletion(\"a\", \"b\")\n    }\n\n    fun testAfterEachNamespace() {\n        myFixture.configureByFile(\"tests/afterEachNamespace.php\")\n\n        assertCompletion(\"a\", \"b\")\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/types/ThisTypeTest.kt",
    "content": "package com.pestphp.pest.types\n\nclass ThisTypeTest : BaseTypeTestCase() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyDirectoryToProject(\"this\", \"/\")\n    }\n\n    fun testItFunction() {\n        myFixture.configureByFile(\"itTest.php\")\n        assertThisCompletion()\n    }\n\n    fun testTestFunction() {\n        myFixture.configureByFile(\"testTest.php\")\n        assertThisCompletion()\n    }\n\n    fun testShortLambda() {\n        myFixture.configureByFile(\"itShortLambdaTest.php\")\n        assertThisCompletion()\n    }\n\n    fun testBeforeEach() {\n        myFixture.configureByFile(\"beforeEach.php\")\n        assertThisCompletion()\n    }\n\n    private fun assertThisCompletion() {\n        assertCompletion(\n            \"expectException\",\n            \"expectExceptionCode\",\n            \"someProtectedMethod\",\n            \"someStaticMethod\",\n            \"protectedField\",\n        )\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/GetPestTestNameTests.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.childrenOfType\nimport com.intellij.testFramework.TestDataPath\nimport com.jetbrains.php.lang.psi.elements.FieldReference\nimport com.jetbrains.php.lang.psi.elements.FunctionReference\nimport com.jetbrains.php.lang.psi.elements.Statement\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isDescribeFunction\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/PestUtil\")\nclass GetPestTestNameTests : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestUtil\"\n\n    fun testFunctionCallNamedItWithDescriptionAndClosure() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndClosure.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertEquals(\"it basic\", testElement.getPestTestName())\n    }\n\n    fun testFunctionCallNamedTestWithDescriptionAndClosure() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithDescriptionAndClosure.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertEquals(\"basic\", testElement.getPestTestName())\n    }\n\n    fun testFunctionCallNamedItWithConcatStringTest() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithConcatString.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertEquals(\"it basic supertest\", testElement.getPestTestName())\n    }\n\n    fun testFunctionCallNamedTestWithConcatStringTest() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithConcatString.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertEquals(\"basic super\", testElement.getPestTestName())\n    }\n\n    fun testFunctionCallNamedDescribeWithDescriptionAndClosure() {\n        val file = myFixture.configureByFile(\"PestDescribeBlock.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertEquals(\"`sum` → \", testElement.getPestTestName())\n    }\n\n    fun testNestedDescribeFunctionCalls() {\n        val file = myFixture.configureByFile(\"NestedDescribeFunctionCalls.php\")\n\n        val testElements = getAllPestTests(file.firstChild)\n\n        listOf(\n            \"`SomeClass` → it works as well\",\n            \"`SomeClass` → `SomeMethod` → it does not work\",\n            \"`SomeClass` → `SomeMethod` → \",\n            \"`SomeClass` → \",\n        ).zip(testElements).toMap().forEach { (expected, describeBlock) ->\n            assertEquals(expected, describeBlock.getPestTestName())\n        }\n    }\n\n    fun testArchFunctionCall() {\n        val file = myFixture.configureByFile(\"PestArchFunctionCall.php\")\n\n        val testElements = file.firstChild.childrenOfType<Statement>().map { getPestTestFieldReference(it.firstChild) }\n\n        listOf(\n            \"preset  → laravel \",\n            \"preset  → laravel  → ignoring 'A'\",\n            \"preset  → laravel  → ignoring ['A']\",\n            \"preset  → laravel  → ignoring ['A']\",\n            \"expect 'src' → toUseStrictTypes  → not → toUse ['die', 'dd', 'dump']\"\n        ).zip(testElements).toMap().forEach { (expected, archTest) ->\n            assertEquals(expected, archTest.getPestTestName())\n        }\n    }\n\n    private fun getPestTestFieldReference(test: PsiElement): PsiElement {\n        when (test.firstChild) {\n            is FunctionReference, is FieldReference -> return getPestTestFieldReference(test.firstChild)\n            else -> return test\n        }\n    }\n\n    private fun getAllPestTests(root: PsiElement): List<PsiElement> {\n        if (root is FunctionReferenceImpl && !root.isDescribeFunction()) return listOf(root)\n        return root.children.fold(mutableListOf<PsiElement>()) { list, element ->\n            list.addAll(\n                getAllPestTests(element) +\n                    if (root is FunctionReferenceImpl && root.isDescribeFunction()) listOf(root) else listOf()\n            )\n            list\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/GetPestTestsTest.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.getPestTests\n\nclass GetPestTestsTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestUtil\"\n\n    fun testMethodCallNamedTestIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedTest.php\")\n\n        assertEmpty(file.getPestTests())\n    }\n\n    fun testMethodCallNamedItIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedIt.php\")\n\n        assertEmpty(file.getPestTests())\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndClosure() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndClosure.php\")\n\n        assertNotEmpty(file.getPestTests())\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndHigherOrder() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndHigherOrder.php\")\n\n        assertNotEmpty(file.getPestTests())\n    }\n\n    fun testFunctionCallNamedTestWithDescriptionAndHigherOrder() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithDescriptionAndHigherOrder.php\")\n\n        assertNotEmpty(file.getPestTests())\n    }\n\n    fun testMethodCallNamedItAndVariableTestIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedItAndVariableTest.php\")\n\n        assertEmpty(file.getPestTests())\n    }\n\n    fun testPestTestWithNamespaceIsPestTest() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithNamesapce.php\")\n\n        assertNotEmpty(file.getPestTests())\n    }\n\n    fun testNestedDescribeBlockTestsAreIncluded() {\n        val file = myFixture.configureByFile(\"NestedDescribeFunctionCalls.php\")\n\n        val tests = file.getPestTests()\n        assertEquals(4, tests.size)\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/IsPestTestFileTest.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.isPestTestFile\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/PestUtil\")\nclass IsPestTestFileTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestUtil\"\n\n    fun testMethodCallNamedTestIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedTest.php\")\n\n        assertFalse(file.isPestTestFile())\n    }\n\n    fun testMethodCallNamedItIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedIt.php\")\n\n        assertFalse(file.isPestTestFile())\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndClosure() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndClosure.php\")\n\n        assertTrue(file.isPestTestFile())\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndHigherOrder() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndHigherOrder.php\")\n\n        assertTrue(file.isPestTestFile())\n    }\n\n    fun testFunctionCallNamedTestWithDescriptionAndHigherOrder() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithDescriptionAndHigherOrder.php\")\n\n        assertTrue(file.isPestTestFile())\n    }\n\n    fun testMethodCallNamedItAndVariableTestIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedItAndVariableTest.php\")\n\n        assertFalse(file.isPestTestFile())\n    }\n\n    fun testPestTestWithNamespaceIsPestTest() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithNamesapce.php\")\n\n        assertTrue(file.isPestTestFile())\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/IsPestTestFunctionTest.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.intellij.testFramework.TestDataPath\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.isPestTestReference\n\n@TestDataPath(\"\\$CONTENT_ROOT/../resources/com/pestphp/pest/PestUtil\")\nclass IsPestTestFunctionTest : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestUtil\"\n\n    fun testMethodCallNamedTestIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedTest.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertFalse(testElement.isPestTestReference())\n    }\n\n    fun testMethodCallNamedItIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedIt.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertFalse(testElement.isPestTestReference())\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndClosure() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndClosure.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertTrue(testElement.isPestTestReference())\n    }\n\n    fun testFunctionCallNamedItWithDescriptionAndHigherOrder() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithDescriptionAndHigherOrder.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertTrue(testElement.isPestTestReference())\n    }\n\n    fun testFunctionCallNamedTestWithDescriptionAndHigherOrder() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithDescriptionAndHigherOrder.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertTrue(testElement.isPestTestReference())\n    }\n\n    fun testMethodCallNamedItAndVariableTestIsNotPestTest() {\n        val file = myFixture.configureByFile(\"MethodCallNamedItAndVariableTest.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertFalse(testElement.isPestTestReference())\n    }\n\n    fun testFunctionCallNamedItWithConcatStringTest() {\n        val file = myFixture.configureByFile(\"PestItFunctionCallWithConcatString.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertTrue(testElement.isPestTestReference())\n    }\n\n    fun testFunctionCallNamedTestWithConcatStringTest() {\n        val file = myFixture.configureByFile(\"PestTestFunctionCallWithConcatString.php\")\n\n        val testElement = file.firstChild.lastChild.firstChild\n\n        assertTrue(testElement.isPestTestReference())\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/PestUtilTest.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.getPestTestName\nimport com.pestphp.pest.isPestTestFunction\nimport java.util.stream.Collectors\n\nclass PestUtilTest : PestLightCodeFixture() {\n    override fun setUp() {\n        super.setUp()\n\n        myFixture.copyFileToProject(\"SimpleTest.php\")\n    }\n\n    override fun getBasePath(): String = \"${super.getBasePath()}/utilTests\"\n\n    fun testCanGetTestName() {\n        val file = myFixture.configureByFile(\"SimpleTest.php\")\n\n        val functions = PsiTreeUtil.findChildrenOfType(file, FunctionReferenceImpl::class.java)\n            .stream().filter(FunctionReferenceImpl::isPestTestFunction)\n            .collect(Collectors.toList())\n\n        assertEquals(1, functions.count())\n\n        assertEquals(\n            \"it basic\",\n            functions.first().getPestTestName()\n        )\n    }\n\n    fun testClassNameResolutionTestName() {\n        val file = myFixture.configureByFile(\"ClassNameResolutionTest.php\")\n\n        val functions = PsiTreeUtil.findChildrenOfType(file, FunctionReferenceImpl::class.java)\n            .filter(FunctionReferenceImpl::isPestTestFunction)\n\n        assertEquals(1, functions.count())\n\n        assertEquals(\n            \"A\",\n            functions.first().getPestTestName()\n        )\n    }\n\n    fun testClassNameResolutionInNamespaceTestName() {\n        val file = myFixture.configureByFile(\"ClassNameResolutionInNamespaceTest.php\")\n\n        val functions = PsiTreeUtil.findChildrenOfType(file, FunctionReferenceImpl::class.java)\n            .filter(FunctionReferenceImpl::isPestTestFunction)\n\n        assertEquals(1, functions.count())\n\n        assertEquals(\n            \"A\\\\\\\\B\",\n            functions.first().getPestTestName()\n        )\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/ToPestFqnTests.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.toPestFqn\n\nclass ToPestFqnTests : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestUtil\"\n\n    fun testCanGeneratePqn() {\n        val file = myFixture.configureByFile(\n            \"PestItFunctionCallWithDescriptionAndClosure.php\",\n        )\n\n        val testElement = file?.firstChild?.lastChild?.firstChild\n\n        val pqn = testElement?.toPestFqn()\n\n        assertTrue(pqn?.any { it.contains(\"::\") } == true)\n        assertTrue(pqn?.any { it.contains(\"pest_qn:\") } == true)\n    }\n}\n"
  },
  {
    "path": "src/test/kotlin/com/pestphp/pest/utilTests/ToPestTestRegexTests.kt",
    "content": "package com.pestphp.pest.utilTests\n\nimport com.jetbrains.php.util.pathmapper.PhpPathMapper\nimport com.pestphp.pest.PestLightCodeFixture\nimport com.pestphp.pest.toPestTestRegex\n\nclass ToPestTestRegexTests : PestLightCodeFixture() {\n    override fun getBasePath(): String = \"${super.getBasePath()}/PestUtil\"\n\n    fun testRegexContainsStartBounds() {\n        val file = myFixture.configureByFile(\n            \"PestItFunctionCallWithDescriptionAndClosure.php\",\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.startsWith('^') == true)\n    }\n\n    fun testRegexContainsPestNamespacePrefix() {\n        val file = myFixture.configureByFile(\n            \"PestItFunctionCallWithDescriptionAndClosure.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.contains(\"P\\\\\\\\\") == true)\n    }\n\n    fun testRegexContainsClassMethodSeparator() {\n        val file = myFixture.configureByFile(\n            \"PestItFunctionCallWithDescriptionAndClosure.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.contains(\"::\") == true)\n    }\n\n    fun testRegexContainsItWhenItFunctionCall() {\n        val file = myFixture.configureByFile(\n            \"PestItFunctionCallWithDescriptionAndClosure.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.contains(\"it\") == true)\n    }\n\n    fun testRegexEscapesParenthesis() {\n        val file = myFixture.configureByFile(\n            \"PestTestFunctionCallWithParenthesis.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.contains(\"\\\\(\") == true)\n        assertTrue(regex?.contains(\"\\\\)\") == true)\n    }\n\n    fun testRegexEscapesCircumflexes() {\n        val file = myFixture.configureByFile(\n            \"PestTestFunctionCallWithCircumflex.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.contains(\"\\\\^\") == true)\n    }\n\n    fun testRegexEscapesPlusAndQuestionMark() {\n        val file = myFixture.configureByFile(\n            \"PestTestWithPlusAndQuestionMark.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertTrue(regex?.contains(\"\\\\+\") == true)\n        assertTrue(regex?.contains(\"\\\\?\") == true)\n    }\n\n    fun testRegexEndOfLine() {\n        val file = myFixture.configureByFile(\n            \"PestDescribeBlockAndTestFunctionEndOfLine.php\"\n        )\n\n        val testElement = file.firstChild?.children?.map { it.firstChild }?.lastOrNull()\n        val describeElement = file.firstChild?.children?.map { it.firstChild }?.firstOrNull()\n\n        val testRegex = testElement?.toPestTestRegex(\"src\")\n        val describeRegex = describeElement?.toPestTestRegex(\"src\")\n\n        assertTrue(testRegex?.endsWith(\"$\") == true)\n        assertTrue(describeRegex?.endsWith(\"$\") == false)\n    }\n\n    fun testRegexHandlesTestSuffix() {\n        val file = myFixture.configureByFile(\n            \"Login.test.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertFalse(regex?.contains(\".test\") == true)\n        assertTrue(regex?.contains(\"Login\") == true)\n    }\n\n    fun testRegexHandlesSpecSuffix() {\n        val file = myFixture.configureByFile(\n            \"User.spec.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertFalse(regex?.contains(\".spec\") == true)\n        assertTrue(regex?.contains(\"User\") == true)\n    }\n\n    fun testRegexHandlesEmojiInPath() {\n        val regex = \"it renders components\".toPestTestRegex(\n            \"src\",\n            \"/src/livewire⚡/Component.php\",\n            PhpPathMapper.create(this.project)\n        )\n\n        assertFalse(regex.contains(\"⚡\"))\n        assertTrue(regex.contains(\"livewire\"))\n        assertTrue(regex.contains(\"Component\"))\n    }\n\n    fun testRegexHandlesDotInDirectoryName() {\n        val file = myFixture.configureByFile(\n            \"dir.name/Test.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertFalse(regex?.contains(\"dir.name\") == true)\n        assertTrue(regex?.contains(\"dirname\") == true)\n    }\n\n    fun testRegexHandlesMultipleDots() {\n        val file = myFixture.configureByFile(\n            \"Login.integration.test.php\"\n        )\n\n        val testElement = file.firstChild?.lastChild?.firstChild\n\n        val regex = testElement?.toPestTestRegex(\"src\")\n\n        assertFalse(regex?.contains(\".integration\") == true)\n        assertFalse(regex?.contains(\".test\") == true)\n        assertTrue(regex?.contains(\"Login\") == true)\n    }\n}\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/Dataset.php",
    "content": "<?php\n\ndataset('emails', [\n    'enunomaduro@gmail.com',\n    'oliver@nybroe.dev'\n]);\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/Pest.php",
    "content": "<?php\n\nnamespace Tests;\n\nuses(TestCase::class)->in('Feature');\nuses(TestCase::class)->in('Unit');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/AssignmentFunctionCallNamedTest.php",
    "content": "<?php\n\nfunction test($description, $closure = null): \\Pest\\PendingCalls\\TestCall {\n}\n\n$x = test(\"name\", function(){\n    expect(1)->toBe(1);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/AssignmentFunctionCallNamedTestWithoutPest.php",
    "content": "<?php\n\nfunction test(Bar $a) {\n}\n\n$generator = test(new Bar());"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/FunctionCallNamedTestAsArgument.php",
    "content": "<?php\n\nfunction test($description, $closure = null): \\Pest\\PendingCalls\\TestCall {\n}\n\nfunction foo($x) {\n}\n\nfoo(test());"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/FunctionCallNamedTestInsideDescribeBlock.php",
    "content": "<?php\n\ndescribe(\"block\", function () {\n    test(\"foo\", function () {\n    });\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/FunctionCallNamedTestInsideTest.php",
    "content": "<?php\n\ntest(\"foo\", function () {\n    test();\n    test()->createMock(\"\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/FunctionCallNamedTestWithoutPest.php",
    "content": "<?php\n\nfunction test(Bar $a) {\n}\n\ntest(new Bar());\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/MethodCallNamedItAndVariableTest.php",
    "content": "<?php\n\nfunction it($description, $closure = null): \\Pest\\PendingCalls\\TestCall {\n}\n\n$test->it(\"\");"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/NamedDataSets.php",
    "content": "<?php\n\ndescribe(\"a set of tests\", function() {\n    it('can run individual data sets', function (int $a, int $b) {\n        //...\n    })->with([\n           \"dataset a\" => [3, 3],\n           \"dataset b\" => [4, 4]\n         ]);\n    it('Can act as summator', function ($a) {\n        $result = (new SimpleCalculator)->add($a, b: 0);\n        expect ($result) ->toBe ( expected: 1);\n    }) ->with([\n        'first' => fn() => array_map (fn() => rand(1,10), range( start: 1, end: 10)) ,\n        'second' => [1],\n    ]);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/PestItFunctionCallWithDescriptionAndClosure.php",
    "content": "<?php\n\nit('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/PestItFunctionCallWithRedefinition.php",
    "content": "<?php\n\nfunction it($description, $closure = null): \\Pest\\PendingCalls\\TestCall {\n}\n\nit('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestTestRunLineMarkerProviderTest/contextProject/tests/Test.php",
    "content": "<?php\n\nit('basic', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/Login.integration.test.php",
    "content": "<?php\n\nit('can do integration login', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/Login.test.php",
    "content": "<?php\n\nit('can login', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/MethodCallNamedIt.php",
    "content": "<?php\n\n$bar->it();"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/MethodCallNamedItAndVariableTest.php",
    "content": "<?php\n\n$test->it();"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/MethodCallNamedTest.php",
    "content": "<?php\n\n$bar->test();"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/NestedDescribeFunctionCalls.php",
    "content": "<?php\n\ndescribe('SomeClass', function () {\n    it('works as well', function () {\n        expect(true)->toBeTrue();\n    });\n\n    describe('SomeMethod', function () {\n        it('does not work', function () {\n            expect(true)->toBeTrue();\n        });\n    });\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestArchFunctionCall.php",
    "content": "<?php\n\narch()->preset()->laravel();\narch()->preset()->laravel()->ignoring(\"A\");\narch()->preset()->laravel()->ignoring([\"A\"]);\narch()->preset()->laravel()->ignoring(array(\"A\"));\narch()->expect('src')->toUseStrictTypes()->not->toUse(['die', 'dd', 'dump']);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestDescribeBlock.php",
    "content": "<?php\n\ndescribe('sum', function () {\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestDescribeBlockAndTestFunctionEndOfLine.php",
    "content": "<?php\n\ndescribe('sum', function () {\n});\n\ntest('sum', function () {\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestItFunctionCallWithConcatString.php",
    "content": "<?php\n\nit('basic ' . \"super\" . \"test\", function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestItFunctionCallWithDescriptionAndClosure.php",
    "content": "<?php\n\nit('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestItFunctionCallWithDescriptionAndHigherOrder.php",
    "content": "<?php\n\nit('lives')->assertTrue(true);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestFunctionCallWithCircumflex.php",
    "content": "<?php\n\ntest('basic ^circumflex test', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestFunctionCallWithConcatString.php",
    "content": "<?php\n\ntest('basic ' . \"su\" . \"per\", function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestFunctionCallWithDescriptionAndClosure.php",
    "content": "<?php\n\ntest('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestFunctionCallWithDescriptionAndHigherOrder.php",
    "content": "<?php\n\ntest('lives')->assertTrue(true);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestFunctionCallWithNamesapce.php",
    "content": "<?php\n\nnamespace Unit;\n\nit('basic', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestFunctionCallWithParenthesis.php",
    "content": "<?php\n\ntest('basic (parenthesis)', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/PestTestWithPlusAndQuestionMark.php",
    "content": "<?php\n\ntest('basic? yes+', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/User.spec.php",
    "content": "<?php\n\nit('can create user', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/PestUtil/dir.name/Test.php",
    "content": "<?php\n\nit('runs test', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/SimpleHigherOrderNotTest.php",
    "content": "<?php\n\nit('lives')->not->assertTrue(true);\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/SimpleHigherOrderTestWithName.php",
    "content": "<?php\n\nit('lives')->assertTrue(true);\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/SimpleScript.php",
    "content": "<?php\n\necho \"simple\";\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/TestWithDataset.php",
    "content": "<?php\n\ndataset('emails', [\n    'enunomaduro@gmail.com',\n    'oliver@nybroe.dev'\n]);\n\nit('basic', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateCustomExpectation.afterDelete.php",
    "content": "<?php\n\nexpect()->extend('toBeOne', function (): bool {\n    return false;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateCustomExpectation.afterNavigate.php",
    "content": "<?php\n\n<caret>expect()->extend('toBeOne', function (): bool {\n    return true;\n});\n\nexpect()->extend('toBeOne', function (): bool {\n    return false;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateCustomExpectation.php",
    "content": "<?php\n\n<warning descr=\"Pest custom expectation names must be unique\">expect()->extend('toBeOne', function (): bool {\n    return true;\n})</warning>;\n\n<warning descr=\"Pest custom expectation names must be unique\">expect()->extend('toBeOne', function (): bool {\n    return false;\n})</warning>;"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateTestName.afterDelete.php",
    "content": "<?php\n\ntest('basic', function () {\n    $this->assertTrue(false);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateTestName.afterNavigate.php",
    "content": "<?php\n\n<caret>test('basic', function () {\n    $this->assertTrue(true);\n});\n\ntest('basic', function () {\n    $this->assertTrue(false);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateTestName.php",
    "content": "<?php\n\n<error descr=\"Pest test names must be unique within a file\">test('basic', function () {\n    $this->assertTrue(true);\n})</error>;\n\n<error descr=\"Pest test names must be unique within a file\">test('basic', function () {\n    $this->assertTrue(false);\n})</error>;"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/DuplicateTestNameInDescribeBlock.php",
    "content": "<?php\n\ndescribe('sum', function () {\n    <error descr=\"Pest test names must be unique within a file\">test('adds numbers', function () {\n        expect(1 + 1)->toBe(2);\n    })</error>;\n\n    <error descr=\"Pest test names must be unique within a file\">test('adds numbers', function () {\n        expect(2 + 2)->toBe(4);\n    })</error>;\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/NoDuplicateCustomExpectation.php",
    "content": "<?php\n\nexpect()->extend('toBeOne', function (): bool {\n    return true;\n});\n\nexpect()->extend('toBeTwo', function (): bool {\n    return true;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/NoDuplicateTestName.php",
    "content": "<?php\n\ntest('One test', function () {\n    $this->assertTrue(true);\n});\n\ntest('Another test', function () {\n    $this->assertTrue(true);\n});\n\nclass A {\n}\n\nclass B {\n}\n\ntest(A::class, function () {\n    $this->assertTrue(true);\n});\n\ntest(B::class, function () {\n    $this->assertTrue(true);\n});\n\nclass C {\n    const c = \"something\";\n}\n\ntest(C::c, function () {\n    $this->assertTrue(true);\n});\n\ntest(C::c, function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/annotator/stub/Functions.php",
    "content": "<?php\n\nfunction test($description, $closure) {\n    return new \\Pest\\PendingCalls\\TestCall(null, \"\");\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/codeInsight/typeInference/ThisInInnerClosure.php",
    "content": "<?php\n\nuses(T::class);\n\ntest('test', function () {\n    expect(someFoo(fn() => <type value=\"\\PHPUnit\\Framework\\TestCase|T\">$this</type>->call()))->toBe(\"bar\");\n});\n\nfunction someFoo($closure) {\n    return $closure();\n}\n\ntrait T {\n    function foo() {\n        return \"bar\";\n    }\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/codeInsight/typeInference/ThisInSubproject/Test.php",
    "content": "<?php\n\ntest('foo', function () {\n    <type value=\"\\PHPUnit\\Framework\\TestCase|TestCase\">$this</type>;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/FileWithPestTest.php",
    "content": "<?php\n<caret>\n\ntest('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/locationProvider/DescribeBlock/subdir/Test.php",
    "content": "<?php\n\ndescribe('sum', function () {\n    test('check true', function () {\n        expect(true)->toBe(true);\n    });\n\n    it('check valid', function () {\n        expect(1)->toBe(1);\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/locationProvider/DescribeBlockIt/subdir/Test.php",
    "content": "<?php\n\ndescribe('sum', function () {\n    test('check true', function () {\n        expect(true)->toBe(true);\n    });\n\n    it('check valid', function () {\n        expect(1)->toBe(1);\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/locationProvider/SubprojectFor1xVersion/subdir/Test.php",
    "content": "<?php\n\ntest(\"test\", function () {\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/locationProvider/SubprojectFor2xVersion/subdir/Test.php",
    "content": "<?php\n\ntest(\"test\", function () {\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/DIRFeature/FeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/DynamicFeature/FeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/Feature/FeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/GlobPattern/DirectoryTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/GlobPattern/FileTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/GlobPattern/FileWithRelativePathTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/GroupedFeature/GroupedFeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/Pest.php",
    "content": "<?php\n\nclass BaseTestCase\n{\n    public function baseTestFunc(){}\n}\n\nclass FeatureTestCase\n{\n    public function featureTestFunc(){}\n}\n\nclass SomeBaseTrait\n{\n    public function someBaseTraitFunc(){}\n}\n\npest()->extend(BaseTestCase::class);\n\npest()->extend(FeatureTestCase::class)->in(\"Feature\");\n\npest()->extend(FeatureTestCase::class, SomeBaseTrait::class)->group(\"some group\")->in(\"GroupedFeature\");\n\npest()->extend(FeatureTestCase::class)->in(__DIR__ . \"/DIRFeature\");\n\npest()->extend(FeatureTestCase::class)->in('../tests/DynamicFeature');\n\npest()->extend(FeatureTestCase::class)->in(\"*/DirectoryTest.php\", \"GlobPattern/Fi*st.php\", \"../tests/GlobPattern/Fi*st.php\");"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/Unit/PestExtendUnitTest.php",
    "content": "<?php\n\nclass SomeTrait {\n    public function traitFunc(){}\n}\n\npest()->extend(SomeTrait::class);\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests/Unit/UnitTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/pest/tests2/Unit/UnitTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/php",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/DIRFeature/FeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/DynamicFeature/FeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/Feature/FeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/GlobPattern/DirectoryTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/GlobPattern/FileTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/GlobPattern/FileWithRelativePathTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/GroupedFeature/GroupedFeatureTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/Pest.php",
    "content": "<?php\n\nclass BaseTestCase\n{\n    public function baseTestFunc(){}\n}\n\nclass FeatureTestCase\n{\n    public function featureTestFunc(){}\n}\n\nclass SomeBaseTrait\n{\n    public function someBaseTraitFunc(){}\n}\n\nuses(BaseTestCase::class);\n\nuses(FeatureTestCase::class)->in(\"Feature\");\n\nuses(FeatureTestCase::class, SomeBaseTrait::class)->group(\"some group\")->in(\"GroupedFeature\");\n\nuses(FeatureTestCase::class)->in(__DIR__ . \"/DIRFeature\");\n\nuses(FeatureTestCase::class)->in('../tests/DynamicFeature');\n\nuses(FeatureTestCase::class)->in(\"*/DirectoryTest.php\", \"GlobPattern/Fi*st.php\", \"../tests/GlobPattern/Fi*st.php\");"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/Unit/UnitTest.php",
    "content": "<?php\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/configuration/uses/Unit/UsesUnitTest.php",
    "content": "<?php\n\nclass SomeTrait {\n    public function traitFunc(){}\n}\n\nuses(SomeTrait::class);\n\nit('has home', function (){\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/CustomExpectation.php",
    "content": "<?php\n\nexpect()->extend('toBeOne', function (): bool {\n    return true;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/CustomExpectationWithParameter.php",
    "content": "<?php\n\nexpect()->extend('toHaveValues', function ($valueA, string $valueB, bool $valueC = true): bool {\n    return true;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/CustomThisExpectation.php",
    "content": "<?php\n\nexpect()->extend('toThis', function () {\n    return $this->toBeEmpty();\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/CustomUserExpectation.php",
    "content": "<?php\n\nnamespace App\\Models;\n\nclass User {\n\n}\n\nexpect()->extend('createUser', function (): User {\n    return new User();\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/UnfinishedCustomExpectation.php",
    "content": "<?php\n\nexpect()->extend('toBeOne');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/generators/ExpectationGenerator/GeneratedWithMethod.php",
    "content": "<?php\n\nnamespace Pest;\n\n/**\n * @method bool works()\n */\nfinal class Expectation {}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/customExpectations/subFolder/CustomExpectation.php",
    "content": "<?php\n\nexpect()->extend('toBeExpectationFromSubFolder', function (): bool {\n    return true;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/configuration/pest/CompleteFakePestInFolder.php",
    "content": "<?php\n\nnamespace Tests;\n\nnotPest(TestCase::class)->in('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/configuration/pest/CompleteInFolder.php",
    "content": "<?php\n\nnamespace Tests;\n\npest()->extend(TestCase::class)->in('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/configuration/pest/Test.php",
    "content": "<?php\n\nit('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/configuration/uses/CompleteFakeInFolder.php",
    "content": "<?php\n\nnamespace Tests;\n\nnotUses(TestCase::class)->in('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/configuration/uses/CompleteInFolder.php",
    "content": "<?php\n\nnamespace Tests;\n\nuses(TestCase::class)->in('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/configuration/uses/Test.php",
    "content": "<?php\n\nit('basic', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/AutocompleteDatasetTest.php",
    "content": "<?php\n\ndataset('dojos', [\n    'Cobra Kai',\n    'Eagle Fang',\n    'Miyagi Do'\n]);\n\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with('<caret>');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetInDescribeBlock.php",
    "content": "<?php\n\ndataset('top_level', [1, 2, 3]);\n\ndescribe('my block', function() {\n    dataset('inside_describe', [4, 5, 6]);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetInDescribeBlockCompletion.php",
    "content": "<?php\n\ndescribe('my block', function() {\n    test('check valid', function ($number) {\n        expect($number)->toBe(1);\n    })->with('<caret>');\n\n    dataset('some_numbers', function () {\n        return [1, 2];\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetInDescribeBlockReference.php",
    "content": "<?php\n\ndescribe('my block', function() {\n    test('check valid', function ($number) {\n        expect($number)->toBe(1);\n    })->with('some_numbers<caret>');\n\n    dataset('some_numbers', function () {\n        return [1, 2];\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetInsideDescribeBlockTest.php",
    "content": "<?php\n\ndescribe('my block', function() {\n    test('is delicious', function() {\n        expect(true)->toBe(true);\n    });\n\n    test('check valid', function ($number) {\n        expect($number)->toBe(1);\n    })->with('some_numbers');\n\n    dataset('some_numbers', function () {\n        return [\n            1,\n            2\n        ];\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetNoArgsTest.php",
    "content": "<?php\n\ndataset('dojos', [\n    'Cobra Kai',\n    'Eagle Fang',\n    'Miyagi Do'\n]);\n\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with();\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetOnNonPestTest.php",
    "content": "<?php\n\nsomething('can check if in the valley', function (string $dojo) {\n\n})->with('stances<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetOnNonPestTestCompletion.php",
    "content": "<?php\n\nsomething('can check if in the valley', function (string $dojo) {\n\n})->with('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetReference.php",
    "content": "<?php\n\ndataset('dojos', [\n    'Cobra Kai',\n    'Eagle Fang',\n    'Miyagi Do'\n]);\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with('dojos<caret>');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DatasetTest.php",
    "content": "<?php\n\ndataset('dojos', [\n    'Cobra Kai',\n    'Eagle Fang',\n    'Miyagi Do'\n]);\n\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with('dojos');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/Datasets.php",
    "content": "<?php\n\ndataset('stances', [\n    'Seiza',\n    'Musubi Dachi',\n    'Heisoku Dachi',\n    'Heiko Dachi',\n    'Zenkutsu Dachi',\n    'Han Zenkutsu Dachi',\n    'Kokutsu Dachi',\n]);\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/DoubleWithDatasetReference.php",
    "content": "<?php\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with([\n    Bar::class,\n    Restaurant::class,\n])->with('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/InvalidDatasetInDescribeBlockTest.php",
    "content": "<?php\n\ndataset('numbers', [1, 2, 3]);\n\ndescribe('math', function () {\n    it('can add numbers', function (int $number) {\n        expect($number)->toBeInt();\n    })->with(<error descr=\"The dataset does not exist\">'invalid_dataset'</error>);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/InvalidDatasetNameCase.after.php",
    "content": "<?php\n\ntest('test 1', function ($number) {\n    expect($number)->toBe(1);\n})->with('some numbers');\n\ndataset('some numbers', function () {\n    return [\n        1\n    ];\n});\n\ntest('test 2', function ($number) {\n    expect($number)->toBe(2);\n})->with('some numbers');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/InvalidDatasetNameCase.php",
    "content": "<?php\n\ntest('test 1', function ($number) {\n    expect($number)->toBe(1);\n})->with('some_numbers');\n\ndataset(<weak_warning descr=\"Words in Pest dataset names should be separated by spaces\">'some_numbers'</weak_warning>, function () {\n    return [\n        1\n    ];\n});\n\ntest('test 2', function ($number) {\n    expect($number)->toBe(2);\n})->with('some_numbers');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/InvalidDatasetTest.php",
    "content": "<?php\n\ndataset('dojos', [\n    'Cobra Kai',\n    'Eagle Fang',\n    'Miyagi Do'\n]);\n\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with(<error descr=\"The dataset does not exist\">'dodos'</error>);\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/NotDatasetReference.php",
    "content": "<?php\n\nit('can check if in the valley', function (string $dojo) {\n\n})->notDataset('<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/SharedDatasetReference.php",
    "content": "<?php\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with('stances<caret>');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/datasets/ValidDatasetNameCase.php",
    "content": "<?php\n\ndataset('some numbers', function () {\n    return [\n        1\n    ];\n});\n\ndataset('some.numbers', function () {\n    return [\n        1\n    ];\n});\n\ndataset('some-numbers', function () {\n    return [\n        1\n    ];\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/parallel/ATest.php",
    "content": "<?php\n\ntest(\"foo\", function() {});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/features/parallel/php",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/generateTest/testWithNamespace.after.php",
    "content": "<?php\n\nnamespace a;\n\ntest('foo', function () {\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/generateTest/testWithNamespace.php",
    "content": "<?php\n\nclass A  {\n    function foo(): int {\n        return 1;\n    }\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/goto/PestTestFinder/App/User.php",
    "content": "<?php\n\nnamespace App;\n\nclass User {\n    public function getName(): String\n    {\n       return \"Oliver Nybroe\";\n    }\n\n    public function isPestDeveloper(): bool\n    {\n        return true;\n    }\n\n    public function isPest(): bool\n    {\n        return true;\n    }\n\n    public function is(): bool\n    {\n        return true;\n    }\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/goto/PestTestFinder/test/App/MockTest.php",
    "content": "<?php\n\nuse App\\User;\n\nclass Mock {\n    public function getMock() {\n    }\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/goto/PestTestFinder/test/App/UserDescribeTest.php",
    "content": "<?php\n\nuse App\\User;\n\ndescribe('User', function () {\n    test(\"is pest developer in describe\", function () {\n       $user = new User();\n\n       $this->assertTrue($user->isPestDeveloper());\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/goto/PestTestFinder/test/App/UserTest.php",
    "content": "<?php\n\nuse App\\User;\n\ntest(\"Can get user's name\", function () {\n   $user = new User();\n\n   $this->assertEquals(\"Oliver Nybroe\", $user->getName());\n});\n\ntest(\"is pest developer\", function () {\n   $user = new User();\n\n   $this->assertTrue($user->isPestDeveloper());\n});\n\ntest(\"is pest developer check if not false\", function () {\n   $user = new User();\n\n   $this->assertNotEquals(false, $user->isPestDeveloper());\n});\n\ntest(\"incorrect is pest developer\", function () {\n   $user = new User();\n\n   $this->assertNotEquals(false, $user->isPestDeveloper());\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/goto/datasetUsages/DatasetDeclaration.php",
    "content": "<?php\n\ndataset('<caret>datasetName', [\n    'Seiza',\n    'Musubi Dachi',\n    'Heisoku Dachi',\n    'Heiko Dachi',\n    'Zenkutsu Dachi',\n    'Han Zenkutsu Dachi',\n    'Kokutsu Dachi',\n]);\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with('datasetName');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/goto/datasetUsages/DatasetUsage.php",
    "content": "<?php\n\nit('can check if in the valley', function (string $dojo) {\n\n})->with([])->with('datasetName');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/.phpstorm.meta.php",
    "content": "<?php\n\n/**\n * The PHPStorm meta file for Pest.\n */\n\nnamespace PHPSTORM_META {\n    override(\\expect(0), type(0));\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectMethodAssertionCompletion.php",
    "content": "<?php\n\nclass Example {\n    public function getTest(): string\n    {\n        return \"works\";\n    }\n\n    public function getOtherExample(): string\n    {\n        return \"example\";\n    }\n}\n\n$example = new Example();\n\nexpect($example)->getTest()-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectMethodAssertionCompletionChained.php",
    "content": "<?php\n\nclass Example {\n    public function getDate(): DateTime\n    {\n        return new DateTime();\n    }\n}\n\n$example = new Example();\n\nexpect($example)->getDate()->getTimestamp()-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectMethodAssertionCompletionChainedAssertions.php",
    "content": "<?php\n\nclass Example {\n    public function getTest(): string\n    {\n        return \"works\";\n    }\n\n    public function getOtherExample(): string\n    {\n        return \"example\";\n    }\n}\n\n$example = new Example();\n\nexpect($example)\n    ->getTest()->toBeTrue()\n    ->getOtherExample()-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectMethodCompletion.php",
    "content": "<?php\n\nclass Example {\n    public function getTest(): string\n    {\n        return \"works\";\n    }\n\n    public function getOtherExample(): string\n    {\n        return \"example\";\n    }\n}\n\n$example = new Example();\n\nexpect($example)-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectMethodCompletionChained.php",
    "content": "<?php\n\nclass Chained\n{\n    public function getTimestamp()\n    {\n    }\n}\n\nclass Example\n{\n    public function getDate(): Chained\n    {\n        return new Chained();\n    }\n}\n\n$example = new Example();\n\nexpect($example)->getDate()-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectMethodCompletionChainedAssertions.php",
    "content": "<?php\n\nclass Example {\n    public function getTest(): string\n    {\n        return \"works\";\n    }\n\n    public function getOtherExample(): string\n    {\n        return \"example\";\n    }\n}\n\n$example = new Example();\n\nexpect($example)\n    ->getTest()->toBeString()\n    -><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectPropertyAssertionCompletion.php",
    "content": "<?php\n\nclass Example {\n    public string $test = \"works\";\n    public string $otherExample = \"example\";\n}\n\n$example = new Example();\n\nexpect($example)->test-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectPropertyAssertionCompletionChained.php",
    "content": "<?php\n\nclass Example {\n    public DateTime $test;\n\n    public function __construct()\n    {\n        $this->test = new DateTime();\n    }\n}\n\n$example = new Example();\n\nexpect($example)->test->getTimestamp()-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectPropertyAssertionCompletionChainedAssertions.php",
    "content": "<?php\n\nclass Example {\n    public string $test = \"works\";\n    public string $otherExample = \"example\";\n}\n\n$example = new Example();\n\nexpect($example)\n    ->test->toBeString()\n    ->otherExample-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectPropertyCompletion.php",
    "content": "<?php\n\nclass Example {\n    public string $test = \"works\";\n    public string $otherExample = \"example\";\n}\n\n$example = new Example();\n\nexpect($example)-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectPropertyCompletionChained.php",
    "content": "<?php\n\nclass Chained\n{\n    public function getTimestamp()\n    {\n    }\n}\n\nclass Example {\n    public Chained $date;\n\n    public function __construct()\n    {\n        $this->date = new Chained();\n    }\n}\n\n$example = new Example();\n\nexpect($example)->date-><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/ExpectPropertyCompletionChainedAssertions.php",
    "content": "<?php\n\nclass Example {\n    public string $test = \"works\";\n    public string $otherExample = \"example\";\n}\n\n$example = new Example();\n\nexpect($example)\n    ->test->toBeString()\n    -><caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/higherOrderExpectations/stubs.php",
    "content": "<?php\n\nuse Pest\\Expectation;\n\nfunction expect($value = null)\n{\n    return new Expectation($value);\n}\n\nnamespace Pest {\n\n    /**\n     * @internal\n     *\n     * @template TValue\n     *\n     * @property Expectation $not  Creates the opposite expectation.\n     * @property Each        $each Creates an expectation on each element on the traversable value.\n     */\n    final class Expectation\n    {\n        use Extendable {\n            __call as __extendsCall;\n        }\n        use RetrievesValues;\n\n        /**\n         * The expectation value.\n         *\n         * @readonly\n         *\n         * @var mixed\n         */\n        public $value;\n\n        /**\n         * The exporter instance, if any.\n         *\n         * @readonly\n         *\n         * @var Exporter|null\n         */\n        private $exporter;\n\n        /**\n         * Creates a new expectation.\n         *\n         * @param TValue $value\n         */\n        public function __construct($value)\n        {\n            $this->value = $value;\n        }\n\n        /**\n         * Creates a new expectation.\n         *\n         * @param TValue $value\n         *\n         * @return Expectation<TValue>\n         */\n        public function and($value): Expectation\n        {\n            return new self($value);\n        }\n\n        /**\n         * Creates a new expectation with the decoded JSON value.\n         */\n        public function json(): Expectation\n        {\n            return $this->toBeJson()->and(json_decode($this->value, true));\n        }\n\n        /**\n         * Dump the expectation value and end the script.\n         *\n         * @param mixed $arguments\n         *\n         * @return never\n         */\n        public function dd(...$arguments): void\n        {\n            if (function_exists('dd')) {\n                dd($this->value, ...$arguments);\n            }\n\n            var_dump($this->value);\n\n            exit(1);\n        }\n\n        /**\n         * Send the expectation value to Ray along with all given arguments.\n         *\n         * @param mixed $arguments\n         */\n        public function ray(...$arguments): self\n        {\n            if (function_exists('ray')) {\n                ray($this->value, ...$arguments);\n            }\n\n            return $this;\n        }\n\n        /**\n         * Creates the opposite expectation for the value.\n         */\n        public function not(): OppositeExpectation\n        {\n            return new OppositeExpectation($this);\n        }\n\n        /**\n         * Creates an expectation on each item of the iterable \"value\".\n         */\n        public function each(callable $callback = null): Each\n        {\n            if (!is_iterable($this->value)) {\n                throw new BadMethodCallException('Expectation value is not iterable.');\n            }\n\n            if (is_callable($callback)) {\n                foreach ($this->value as $item) {\n                    $callback(new self($item));\n                }\n            }\n\n            return new Each($this);\n        }\n\n        /**\n         * Allows you to specify a sequential set of expectations for each item in a iterable \"value\".\n         *\n         * @template TSequenceValue\n         *\n         * @param callable(self, self): void|TSequenceValue ...$callbacks\n         */\n        public function sequence(...$callbacks): Expectation\n        {\n            if (!is_iterable($this->value)) {\n                throw new BadMethodCallException('Expectation value is not iterable.');\n            }\n\n            $value          = is_array($this->value) ? $this->value : iterator_to_array($this->value);\n            $keys           = array_keys($value);\n            $values         = array_values($value);\n            $callbacksCount = count($callbacks);\n\n            $index = 0;\n\n            while (count($callbacks) < count($values)) {\n                $callbacks[] = $callbacks[$index];\n                $index       = $index < count($values) - 1 ? $index + 1 : 0;\n            }\n\n            if ($callbacksCount > count($values)) {\n                Assert::assertLessThanOrEqual(count($value), count($callbacks));\n            }\n\n            foreach ($values as $key => $item) {\n                if ($callbacks[$key] instanceof Closure) {\n                    call_user_func($callbacks[$key], new self($item), new self($keys[$key]));\n                    continue;\n                }\n\n                (new self($item))->toEqual($callbacks[$key]);\n            }\n\n            return $this;\n        }\n\n        /**\n         * If the subject matches one of the given \"expressions\", the expression callback will run.\n         *\n         * @template TMatchSubject of array-key\n         *\n         * @param callable(): TMatchSubject|TMatchSubject $subject\n         * @param array<TMatchSubject, (callable(Expectation<TValue>): mixed)|TValue> $expressions\n         */\n        public function match($subject, array $expressions): Expectation\n        {\n            $subject = is_callable($subject)\n                ? $subject\n                : function () use ($subject) {\n                    return $subject;\n                };\n\n            $subject   = $subject();\n\n            $matched = false;\n\n            foreach ($expressions as $key => $callback) {\n                if ($subject != $key) {\n                    continue;\n                }\n\n                $matched = true;\n\n                if (is_callable($callback)) {\n                    $callback(new self($this->value));\n                    continue;\n                }\n\n                $this->and($this->value)->toEqual($callback);\n\n                break;\n            }\n\n            if ($matched === false) {\n                throw new ExpectationFailedException('Unhandled match value.');\n            }\n\n            return $this;\n        }\n\n        /**\n         * Apply the callback if the given \"condition\" is falsy.\n         *\n         * @param  (callable(): bool)|bool $condition\n         * @param callable(Expectation<TValue>): mixed $callback\n         */\n        public function unless($condition, callable $callback): Expectation\n        {\n            $condition = is_callable($condition)\n                ? $condition\n                : static function () use ($condition): bool {\n                    return (bool) $condition; // @phpstan-ignore-line\n                };\n\n            return $this->when(!$condition(), $callback);\n        }\n\n        /**\n         * Apply the callback if the given \"condition\" is truthy.\n         *\n         * @param  (callable(): bool)|bool $condition\n         * @param callable(Expectation<TValue>): mixed $callback\n         */\n        public function when($condition, callable $callback): Expectation\n        {\n            $condition = is_callable($condition)\n                ? $condition\n                : static function () use ($condition): bool {\n                    return (bool) $condition; // @phpstan-ignore-line\n                };\n\n            if ($condition()) {\n                $callback($this->and($this->value));\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that two variables have the same type and\n         * value. Used on objects, it asserts that two\n         * variables reference the same object.\n         *\n         * @param mixed $expected\n         */\n        public function toBe($expected): Expectation\n        {\n            Assert::assertSame($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is empty.\n         */\n        public function toBeEmpty(): Expectation\n        {\n            Assert::assertEmpty($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is true.\n         */\n        public function toBeTrue(): Expectation\n        {\n            Assert::assertTrue($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is truthy.\n         */\n        public function toBeTruthy(): Expectation\n        {\n            Assert::assertTrue((bool) $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is false.\n         */\n        public function toBeFalse(): Expectation\n        {\n            Assert::assertFalse($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is falsy.\n         */\n        public function toBeFalsy(): Expectation\n        {\n            Assert::assertFalse((bool) $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is greater than $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeGreaterThan($expected): Expectation\n        {\n            Assert::assertGreaterThan($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is greater than or equal to $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeGreaterThanOrEqual($expected): Expectation\n        {\n            Assert::assertGreaterThanOrEqual($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is less than or equal to $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeLessThan($expected): Expectation\n        {\n            Assert::assertLessThan($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is less than $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeLessThanOrEqual($expected): Expectation\n        {\n            Assert::assertLessThanOrEqual($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that $needle is an element of the value.\n         *\n         * @param mixed $needles\n         */\n        public function toContain(...$needles): Expectation\n        {\n            foreach ($needles as $needle) {\n                if (is_string($this->value)) {\n                    Assert::assertStringContainsString($needle, $this->value);\n                } else {\n                    Assert::assertContains($needle, $this->value);\n                }\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value starts with $expected.\n         */\n        public function toStartWith(string $expected): Expectation\n        {\n            Assert::assertStringStartsWith($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value ends with $expected.\n         */\n        public function toEndWith(string $expected): Expectation\n        {\n            Assert::assertStringEndsWith($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that $number matches value's Length.\n         */\n        public function toHaveLength(int $number): Expectation\n        {\n            if (is_string($this->value)) {\n                Assert::assertEquals($number, mb_strlen($this->value));\n\n                return $this;\n            }\n\n            if (is_iterable($this->value)) {\n                return $this->toHaveCount($number);\n            }\n\n            if (is_object($this->value)) {\n                if (method_exists($this->value, 'toArray')) {\n                    $array = $this->value->toArray();\n                } else {\n                    $array = (array) $this->value;\n                }\n\n                Assert::assertCount($number, $array);\n\n                return $this;\n            }\n\n            throw new BadMethodCallException('Expectation value length is not countable.');\n        }\n\n        /**\n         * Asserts that $count matches the number of elements of the value.\n         */\n        public function toHaveCount(int $count): Expectation\n        {\n            Assert::assertCount($count, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value contains the property $name.\n         *\n         * @param mixed $value\n         */\n        public function toHaveProperty(string $name, $value = null): Expectation\n        {\n            $this->toBeObject();\n\n            Assert::assertTrue(property_exists($this->value, $name));\n\n            if (func_num_args() > 1) {\n                /* @phpstan-ignore-next-line */\n                Assert::assertEquals($value, $this->value->{$name});\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value contains the provided properties $names.\n         *\n         * @param iterable<array-key, string> $names\n         */\n        public function toHaveProperties(iterable $names): Expectation\n        {\n            foreach ($names as $name) {\n                $this->toHaveProperty($name);\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that two variables have the same value.\n         *\n         * @param mixed $expected\n         */\n        public function toEqual($expected): Expectation\n        {\n            Assert::assertEquals($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that two variables have the same value.\n         * The contents of $expected and the $this->value are\n         * canonicalized before they are compared. For instance, when the two\n         * variables $expected and $this->value are arrays, then these arrays\n         * are sorted before they are compared. When $expected and $this->value\n         * are objects, each object is converted to an array containing all\n         * private, protected and public attributes.\n         *\n         * @param mixed $expected\n         */\n        public function toEqualCanonicalizing($expected): Expectation\n        {\n            Assert::assertEqualsCanonicalizing($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the absolute difference between the value and $expected\n         * is lower than $delta.\n         *\n         * @param mixed $expected\n         */\n        public function toEqualWithDelta($expected, float $delta): Expectation\n        {\n            Assert::assertEqualsWithDelta($expected, $this->value, $delta);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is one of the given values.\n         *\n         * @param iterable<int|string, mixed> $values\n         */\n        public function toBeIn(iterable $values): Expectation\n        {\n            Assert::assertContains($this->value, $values);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is infinite.\n         */\n        public function toBeInfinite(): Expectation\n        {\n            Assert::assertInfinite($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is an instance of $class.\n         *\n         * @param string $class\n         */\n        public function toBeInstanceOf($class): Expectation\n        {\n            /* @phpstan-ignore-next-line */\n            Assert::assertInstanceOf($class, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is an array.\n         */\n        public function toBeArray(): Expectation\n        {\n            Assert::assertIsArray($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type bool.\n         */\n        public function toBeBool(): Expectation\n        {\n            Assert::assertIsBool($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type callable.\n         */\n        public function toBeCallable(): Expectation\n        {\n            Assert::assertIsCallable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type float.\n         */\n        public function toBeFloat(): Expectation\n        {\n            Assert::assertIsFloat($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type int.\n         */\n        public function toBeInt(): Expectation\n        {\n            Assert::assertIsInt($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type iterable.\n         */\n        public function toBeIterable(): Expectation\n        {\n            Assert::assertIsIterable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type numeric.\n         */\n        public function toBeNumeric(): Expectation\n        {\n            Assert::assertIsNumeric($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type object.\n         */\n        public function toBeObject(): Expectation\n        {\n            Assert::assertIsObject($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type resource.\n         */\n        public function toBeResource(): Expectation\n        {\n            Assert::assertIsResource($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type scalar.\n         */\n        public function toBeScalar(): Expectation\n        {\n            Assert::assertIsScalar($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type string.\n         */\n        public function toBeString(): Expectation\n        {\n            Assert::assertIsString($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a JSON string.\n         */\n        public function toBeJson(): Expectation\n        {\n            Assert::assertIsString($this->value);\n            Assert::assertJson($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is NAN.\n         */\n        public function toBeNan(): Expectation\n        {\n            Assert::assertNan($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is null.\n         */\n        public function toBeNull(): Expectation\n        {\n            Assert::assertNull($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value array has the provided $key.\n         *\n         * @param string|int $key\n         * @param mixed      $value\n         */\n        public function toHaveKey($key, $value = null): Expectation\n        {\n            if (is_object($this->value) && method_exists($this->value, 'toArray')) {\n                $array = $this->value->toArray();\n            } else {\n                $array = (array) $this->value;\n            }\n\n            try {\n                Assert::assertTrue(Arr::has($array, $key));\n\n                /* @phpstan-ignore-next-line  */\n            } catch (ExpectationFailedException $exception) {\n                throw new ExpectationFailedException(\"Failed asserting that an array has the key '$key'\", $exception->getComparisonFailure());\n            }\n\n            if (func_num_args() > 1) {\n                Assert::assertEquals($value, Arr::get($array, $key));\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value array has the provided $keys.\n         *\n         * @param array<int, int|string> $keys\n         */\n        public function toHaveKeys(array $keys): Expectation\n        {\n            foreach ($keys as $key) {\n                $this->toHaveKey($key);\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a directory.\n         */\n        public function toBeDirectory(): Expectation\n        {\n            Assert::assertDirectoryExists($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a directory and is readable.\n         */\n        public function toBeReadableDirectory(): Expectation\n        {\n            Assert::assertDirectoryIsReadable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a directory and is writable.\n         */\n        public function toBeWritableDirectory(): Expectation\n        {\n            Assert::assertDirectoryIsWritable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a file.\n         */\n        public function toBeFile(): Expectation\n        {\n            Assert::assertFileExists($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a file and is readable.\n         */\n        public function toBeReadableFile(): Expectation\n        {\n            Assert::assertFileIsReadable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a file and is writable.\n         */\n        public function toBeWritableFile(): Expectation\n        {\n            Assert::assertFileIsWritable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value array matches the given array subset.\n         *\n         * @param array<int|string, mixed> $array\n         */\n        public function toMatchArray($array): Expectation\n        {\n            if (is_object($this->value) && method_exists($this->value, 'toArray')) {\n                $valueAsArray = $this->value->toArray();\n            } else {\n                $valueAsArray = (array) $this->value;\n            }\n\n            foreach ($array as $key => $value) {\n                Assert::assertArrayHasKey($key, $valueAsArray);\n\n                Assert::assertEquals(\n                    $value,\n                    $valueAsArray[$key],\n                    sprintf(\n                        'Failed asserting that an array has a key %s with the value %s.',\n                        $this->export($key),\n                        $this->export($valueAsArray[$key]),\n                    ),\n                );\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value object matches a subset\n         * of the properties of an given object.\n         *\n         * @param array<string, mixed>|object $object\n         */\n        public function toMatchObject($object): Expectation\n        {\n            foreach ((array) $object as $property => $value) {\n                Assert::assertTrue(property_exists($this->value, $property));\n\n                /* @phpstan-ignore-next-line */\n                $propertyValue = $this->value->{$property};\n                Assert::assertEquals(\n                    $value,\n                    $propertyValue,\n                    sprintf(\n                        'Failed asserting that an object has a property %s with the value %s.',\n                        $this->export($property),\n                        $this->export($propertyValue),\n                    ),\n                );\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value matches a regular expression.\n         */\n        public function toMatch(string $expression): Expectation\n        {\n            Assert::assertMatchesRegularExpression($expression, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value matches a constraint.\n         */\n        public function toMatchConstraint(Constraint $constraint): Expectation\n        {\n            Assert::assertThat($this->value, $constraint);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that executing value throws an exception.\n         *\n         * @param (Closure(Throwable): mixed)|string $exception\n         */\n        public function toThrow($exception, string $exceptionMessage = null): Expectation\n        {\n            $callback = NullClosure::create();\n\n            if ($exception instanceof Closure) {\n                $callback   = $exception;\n                $parameters = (new ReflectionFunction($exception))->getParameters();\n\n                if (1 !== count($parameters)) {\n                    throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.');\n                }\n\n                if (!($type = $parameters[0]->getType()) instanceof ReflectionNamedType) {\n                    throw new InvalidArgumentException('The given closure\\'s parameter must be type-hinted as the class string.');\n                }\n\n                $exception = $type->getName();\n            }\n\n            try {\n                ($this->value)();\n            } catch (Throwable $e) { // @phpstan-ignore-line\n                if (!class_exists($exception)) {\n                    Assert::assertStringContainsString($exception, $e->getMessage());\n\n                    return $this;\n                }\n\n                if ($exceptionMessage !== null) {\n                    Assert::assertStringContainsString($exceptionMessage, $e->getMessage());\n                }\n\n                Assert::assertInstanceOf($exception, $e);\n                $callback($e);\n\n                return $this;\n            }\n\n            if (!class_exists($exception)) {\n                throw new ExpectationFailedException(\"Exception with message \\\"{$exception}\\\" not thrown.\");\n            }\n\n            throw new ExpectationFailedException(\"Exception \\\"{$exception}\\\" not thrown.\");\n        }\n\n        /**\n         * Exports the given value.\n         *\n         * @param mixed $value\n         */\n        private function export($value): string\n        {\n            if ($this->exporter === null) {\n                $this->exporter = new Exporter();\n            }\n\n            return $this->exporter->export($value);\n        }\n\n        /**\n         * Dynamically handle calls to the class or\n         * creates a new higher order expectation.\n         *\n         * @param array<int, mixed> $parameters\n         *\n         * @return HigherOrderExpectation|mixed\n         */\n        public function __call(string $method, array $parameters)\n        {\n            if (!static::hasExtend($method)) {\n                /* @phpstan-ignore-next-line */\n                return new HigherOrderExpectation($this, $this->value->$method(...$parameters));\n            }\n\n            return $this->__extendsCall($method, $parameters);\n        }\n\n        /**\n         * Dynamically calls methods on the class without any arguments\n         * or creates a new higher order expectation.\n         *\n         * @return Expectation|HigherOrderExpectation\n         */\n        public function __get(string $name)\n        {\n            if (!method_exists($this, $name) && !static::hasExtend($name)) {\n                return new HigherOrderExpectation($this, $this->retrieve($name, $this->value));\n            }\n\n            /* @phpstan-ignore-next-line */\n            return $this->{$name}();\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/indexers/PestTestIndexTest/FileWithDescribeBlockTest.php",
    "content": "<?php\n\ndescribe('sum', function () {\n    test('adds numbers', function () {\n        expect(1 + 1)->toBe(2);\n    });\n\n    it('handles zero', function () {\n        expect(0 + 0)->toBe(0);\n    });\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/indexers/PestTestIndexTest/FileWithPestTest.php",
    "content": "<?php\n\ntest('basic', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/indexers/PestTestIndexTest/FileWithPestTodosTest.php",
    "content": "<?php\n\ntodo('basic');\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/indexers/PestTestIndexTest/FileWithoutPestTest.php",
    "content": "<?php\n\necho \"works\";"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ExpectCallsWithOtherStatementsBetween.php",
    "content": "<?php\n\nexpect($answer)->toBe(42);\nEarth::tryCalculateQuestion();\nexpect($question)->toBeNull();\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/InvalidTestNameAndDatasetName.after.php",
    "content": "<?php\n\ndataset(name: 'someDataSet', dataset: []);\n\ntest('not sentence case', function () {\n    expect(true)->toBeTrue();\n})->with('someDataSet');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/InvalidTestNameAndDatasetName.php",
    "content": "<?php\n\ndataset(name: 'someDataSet', dataset: []);\n\ntest(<weak_warning descr=\"Words in Pest test names should be separated by spaces\">'NotSentenceCase'</weak_warning>, function () {\n    expect(true)->toBeTrue();\n})->with('someDataSet');"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/InvalidTestNameCase.after.php",
    "content": "<?php\n\ntest('basic test', function () {\n    $this->assertTrue(true);\n});\n\nit('is basic test', function () {\n    $this->assertTrue(true);\n});\n\nit('is.basic test', function () {\n    $this->assertTrue(true);\n});\n\nit('is::basic test', function () {\n    $this->assertTrue(true);\n});\n\nit('is basic.test 2', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/InvalidTestNameCase.php",
    "content": "<?php\n\ntest(<weak_warning descr=\"Words in Pest test names should be separated by spaces\">'basic_test'</weak_warning>, function () {\n    $this->assertTrue(true);\n});\n\nit(<weak_warning descr=\"Words in Pest test names should be separated by spaces\">'is_basic_test'</weak_warning>, function () {\n    $this->assertTrue(true);\n});\n\nit(<weak_warning descr=\"Words in Pest test names should be separated by spaces\">'is.basic_test'</weak_warning>, function () {\n    $this->assertTrue(true);\n});\n\nit(<weak_warning descr=\"Words in Pest test names should be separated by spaces\">'is::basic_test'</weak_warning>, function () {\n    $this->assertTrue(true);\n});\n\nit(<weak_warning descr=\"Words in Pest test names should be separated by spaces\">'isBasic._test2'</weak_warning>, function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ManyExpectCall.after.php",
    "content": "<?php\n\nexpect($answer)->toBe(42)\n    ->and($question)->toBeNull()\n    ->and($question)->toBeNull()\n    ->and($question)->toBeNull()\n    ->and($question)->toBeNull()\n    ->and($question)->toBeNull()\n    ->and($question)->toBeNull();\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ManyExpectCall.php",
    "content": "<?php\n\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($answer)->toBe(42);</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/MultipleExpectCall.after.php",
    "content": "<?php\n\nexpect($answer)->toBe(42)\n    ->and($question)->toBeNull();\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/MultipleExpectCall.php",
    "content": "<?php\n\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($answer)->toBe(42);</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/MultipleExpectCallsWithOtherStatementsBetween.after.php",
    "content": "<?php\n\nexpect(Earth::owner())->toBeInstanceOf(Mouse::class)\n    ->and($answer)->toBe(42);\nEarth::tryCalculateQuestion();\nexpect($question)->toBeNull()\n    ->and(Earth::exists())->toBeFalse();\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/MultipleExpectCallsWithOtherStatementsBetween.php",
    "content": "<?php\n\n<weak_warning descr=\"Multiple expectations can be chained together\">expect(Earth::owner())->toBeInstanceOf(Mouse::class);</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($answer)->toBe(42);</weak_warning>\nEarth::tryCalculateQuestion();\n<weak_warning descr=\"Multiple expectations can be chained together\">expect($question)->toBeNull();</weak_warning>\n<weak_warning descr=\"Multiple expectations can be chained together\">expect(Earth::exists())->toBeFalse();</weak_warning>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/SingleExpectCall.php",
    "content": "<?php\n\nexpect($answer)->toBe(42);\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ValidHigherOrderTestNameCase.php",
    "content": "<?php\n\ntest('basic test')\n    ->expect(true)\n    ->toBeTrue();\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ValidItTestNameWithoutSpaces.php",
    "content": "<?php\n\nit('works', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ValidTestNameCase.php",
    "content": "<?php\n\ntest('basic test', function () {\n    $this->assertTrue(true);\n});\n\ntest('basic.test', function () {\n    $this->assertTrue(true);\n});\n\ntest('basic::test', function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/ValidTestNameWhenWrongCasingOnOneWord.php",
    "content": "<?php\n\ntest('can be used with camelCase', function () {\n    $this->assertTrue(true);\n});\n\ntest('can be used with snake_case or UPPER_CASE', function () {\n    $this->assertTrue(true);\n});\n\ntest('can \"quote_out\" the word', function () {\n    $this->assertTrue(true);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToBeWithFalse.after.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(true)->toBeFalse();\n    expect(true)->toBeFalse(\"message\");\n    expect(true)->toBeFalse(message: \"message\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToBeWithFalse.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeFalse'\">toBe</weak_warning>(false);\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeFalse'\">toBe</weak_warning>(false, \"message\");\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeFalse'\">toBe</weak_warning>(expected: false, message: \"message\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToBeWithNull.after.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(true)->toBeNull();\n    expect(true)->toBeNull(\"message\");\n    expect(true)->toBeNull(message: \"message\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToBeWithNull.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeNull'\">toBe</weak_warning>(null);\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeNull'\">toBe</weak_warning>(null, \"message\");\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeNull'\">toBe</weak_warning>(expected: null, message: \"message\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToBeWithTrue.after.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(true)->toBeTrue();\n    expect(true)->toBeTrue(\"message\");\n    expect(true)->toBeTrue(message: \"message\");\n    expect(true)->toBeTrue();\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToBeWithTrue.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeTrue'\">toBe</weak_warning>(true);\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeTrue'\">toBe</weak_warning>(true, \"message\");\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeTrue'\">toBe</weak_warning>(expected: true, message: \"message\");\n    expect(true)-><weak_warning descr=\"Assertion 'toBe' can be simplified to 'toBeTrue'\">toBe</weak_warning>(TrUe);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToHaveCountWithZero.after.php",
    "content": "<?php\n\ntest('example', function () {\n    expect([1])->toBeEmpty();\n    expect([1])->toBeEmpty(\"message\");\n    expect([1])->toBeEmpty(message: \"message\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/assertionCanBeSimplified/ToHaveCountWithZero.php",
    "content": "<?php\n\ntest('example', function () {\n    expect([1])-><weak_warning descr=\"Assertion 'toHaveCount' can be simplified to 'toBeEmpty'\">toHaveCount</weak_warning>(0);\n    expect([1])-><weak_warning descr=\"Assertion 'toHaveCount' can be simplified to 'toBeEmpty'\">toHaveCount</weak_warning>(0, \"message\");\n    expect([1])-><weak_warning descr=\"Assertion 'toHaveCount' can be simplified to 'toBeEmpty'\">toHaveCount</weak_warning>(count: 0, message: \"message\");\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/AnonymousFunction.php",
    "content": "<?php\n\ntest(\"myTest\", function () {\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">myAssert(function () {\n        echo 1;\n        expect(1)->toBe(2);\n    });</warning>\n});\n\nfunction myAssert(callable $a) {\n    $a();\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/FailedOneLine.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(1);\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">expect(1)->toBe(2);</warning>\n    expect(2)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/LambdaFunction.php",
    "content": "<?php\n\ntest(\"myTest\", function () {\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">myAssert(\n        fn() => expect(1)->toBe(2)\n    );</warning>\n});\n\nfunction myAssert(callable $a) {\n    $a();\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/MismatchLine.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(1);\n    expect(1)->toBe(2);\n    expect(2)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/MultipleStatementsInOneLine.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">expect(1)->toBe(1); expect(1)->toBe(2);</warning>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/OutRange.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(1);\n    expect(1)->toBe(2);\n    expect(2)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/SingleLeafElementReported.php",
    "content": "<?php\n\ntest(\"myTest\", function () {\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">myAssert(function () {\n        echo 1;\n        expect(1)->toBe(2);\n    }\n    );</warning>\n});\n\nfunction myAssert(callable $a) {\n    $a();\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/TypeBefore.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(1);<caret>\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">expect(1)->toBe(2);</warning>\n    expect(2)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/TypeInside.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(1);\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">expect(1)->to<caret>Be(2);</warning>\n    expect(2)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/WithDataSet.php",
    "content": "<?php\n\ntest(\"myTest\", function(int $a) {\n    expect(1)->toBe($a);\n})->with([[1], [2], [3]]);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/WithDataSetAndKeys.php",
    "content": "<?php\n\ntest(\"myTest\", function(int $a) {\n    <warning descr=\"Failed asserting that 1 is identical to 2.\"><warning descr=\"Failed asserting that 1 is identical to 3.\">expect(1)->toBe($a);</warning></warning>\n})->with([\"first dataset\" => [1], \"second dataset\" => [2], \"third dataset\" => [3]]);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/WithDataSetAndSeveralFails.php",
    "content": "<?php\n\ntest(\"myTest\", function(int $a) {\n    <warning descr=\"Failed asserting that 1 is identical to 2.\">expect(1)->toBe($a);</warning>\n    <warning descr=\"Failed asserting that 2 is identical to 1.\">expect(2)->toBe($a);</warning>\n})->with([\"first dataset\" => [1, 1], \"second dataset\" => [2, 2]]);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/pestTestFailedLine/WithNamedDataSet.php",
    "content": "<?php\n\ntest(\"myTest\", function(int $a) {\n    expect(1)->toBe($a);\n})->with(\"my dataset\");\n\ndataset(\"my dataset\", [[1], [2], [3]]);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/phpstorm/MultipleClassesDeclarationsInPestFileTest.php",
    "content": "<?php\n\ntest('example', function () {\n    expect(new TestingClass())\n        ->toBeInstanceOf(TestingClass::class);\n});\n\nclass TestingClass {\n    //\n}\n\nclass AnotherTestingClass {\n    //\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/.pest/snapshots/Feature/ScreenshotSnapshot/it_browser_testing.snap",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/.pest/snapshots/Feature/ScreenshotSnapshotComplexName/it_1__2_3_4_.snap",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/.pest/snapshots/Feature/ScreenshotSnapshotMultiple/it_test.snap",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/.pest/snapshots/Feature/ScreenshotSnapshotMultiple/it_test2.snap",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/.pest/snapshots/Feature/nested/ScreenshotSnapshotNested/nested.snap",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/MissingScreenshotSnapshot.php",
    "content": "<?php\n\nit('browser testing', function () {\n    $page = visit('/')->on()->mobile();\n    $page-><warning descr=\"Missing screenshot snapshot, run the test to create it\">assertScreenshotMatches</warning>();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/MissingScreenshotSnapshotComplexName.php",
    "content": "<?php\n\nit('1_2-3^4!', function () {\n    $page = visit('/');\n    $page-><warning descr=\"Missing screenshot snapshot, run the test to create it\">assertScreenshotMatches</warning>();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/MissingScreenshotSnapshotMultiple.php",
    "content": "<?php\n\nit('test2', function () {\n    $page = visit('/');\n\n    $page-><warning descr=\"Missing screenshot snapshot, run the test to create it\">assertScreenshotMatches</warning>();\n});\n\nit('test', function () {\n    $page = visit('/');\n\n    $page-><warning descr=\"Missing screenshot snapshot, run the test to create it\">assertScreenshotMatches</warning>();\n\n    $page2 = visit('/home');\n\n    $page2-><warning descr=\"Missing screenshot snapshot, run the test to create it\">assertScreenshotMatches</warning>();\n});\n\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/ScreenshotSnapshot.php",
    "content": "<?php\n\nit('browser testing', function () {\n    $page = visit('/')->on()->mobile();\n    $page->assertScreenshotMatches();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/ScreenshotSnapshotComplexName.php",
    "content": "<?php\n\nit('1_2-3^4!', function () {\n    $page = visit('/');\n    $page->assertScreenshotMatches();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/ScreenshotSnapshotMultiple.php",
    "content": "<?php\n\nit('test2', function () {\n    $page = visit('/');\n    $page->assertScreenshotMatches();\n});\n\nit('test', function () {\n    $page = visit('/');\n    $page->assertScreenshotMatches();\n    \n    $page2 = visit('/home');\n    $page2->assertScreenshotMatches();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/nested/MissingScreenshotSnapshotNested.php",
    "content": "<?php\n\ntest('nested', function () {\n    $page = visit('/')->on()->mobile();\n    $page-><warning descr=\"Missing screenshot snapshot, run the test to create it\">assertScreenshotMatches</warning>();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/inspections/screenshotProject/tests/Feature/nested/ScreenshotSnapshotNested.php",
    "content": "<?php\n\ntest('nested', function () {\n    $page = visit('/');\n    $page->assertScreenshotMatches();\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/runner/pestTestStacktraceParser/Multiline.php",
    "content": "<?php\n\ntest(\"myTest\", function () {\n    foo();\n});\n\nfunction foo() {\n    boo();\n}\n\nfunction boo() {\n    expect(1)->toBe(2);\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/runner/pestTestStacktraceParser/OneLine.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n   expect(1)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/runner/pestTestStacktraceParser/OneLineRemote.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n   expect(1)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/runner/pestTestStacktraceParser/OutRangeLineNumber.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/runner/pestTestStacktraceParser/WrongLineNumber.php",
    "content": "<?php\n\ntest(\"myTest\", function() {\n    expect(1)->toBe(2);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/snapshotTesting/allSnapshotAssertions.php",
    "content": "<?php\n\nassertMatchesSnapshot(\"test\");\nassertMatchesFileHashSnapshot(\"test\");\nassertMatchesFileSnapshot(\"test\");\nassertMatchesHtmlSnapshot(\"test\");\nassertMatchesJsonSnapshot(\"test\");\nassertMatchesObjectSnapshot(\"test\");\nassertMatchesTextSnapshot(\"test\");\nassertMatchesXmlSnapshot(\"test\");\nassertMatchesYamlSnapshot(\"test\");"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/snapshotTesting/nonSnapshotAssertions.php",
    "content": "<?php\n\nassertTrue(true);"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/snapshotTesting/snapshotAssertionUseStatement.php",
    "content": "<?php\nuse function Spatie\\Snapshots\\assertMatchesJsonSnapshot;\nuse function Spatie\\Snapshots\\assertMatchesSnapshot;\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/snapshotTesting/snapshotTest.php",
    "content": "<?php\n\nit('renders correctly', function () {\n    $data = \"My super long snapshot data\";\n\n    assertMatchesSnapshot($data);\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/snapshotTesting/tests/__snapshots__/snapshotTest__it_renders_correctly__1.txt",
    "content": ""
  },
  {
    "path": "src/test/resources/com/pestphp/pest/stubs.php",
    "content": "<?php\n\nuse Pest\\Expectation;\nuse Pest\\PendingCalls\\TestCall;\n\nnamespace Pest\\PendingCalls {\n    class TestCall {\n        public function with(Closure|iterable|string ...$data): self\n    }\n}\n\nfunction test(?string $description = null, ?Closure $closure = null): HigherOrderTapProxy|TestCall\n{\n}\n\nfunction it(string $description, ?Closure $closure = null): TestCall\n{\n}\n\nfunction describe(string $description, $tests): TestCall\n{\n}\n\nfunction expect($value = null)\n{\n    return new Expectation($value);\n}\n\nnamespace Pest {\n    /**\n     * @internal\n     *\n     * @property Expectation $not Creates the opposite expectation.\n     */\n    final class Expectation\n    {\n        /**\n         * Creates a new expectation.\n         *\n         * @param mixed $value\n         */\n        public function __construct($value)\n        {\n            $this->value = $value;\n        }\n\n        /**\n         * Creates a new expectation.\n         *\n         * @param mixed $value\n         */\n        public function and($value): Expectation\n        {\n            return new self($value);\n        }\n\n        /**\n         * Creates the opposite expectation for the value.\n         */\n        public function not(): Expectation\n        {\n            return new Expectation($this);\n        }\n\n        /**\n         * Asserts that two variables have the same type and\n         * value. Used on objects, it asserts that two\n         * variables reference the same object.\n         *\n         * @param mixed $expected\n         */\n        public function toBe($expected): Expectation\n        {\n            Assert::assertSame($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is empty.\n         */\n        public function toBeEmpty(): Expectation\n        {\n            Assert::assertEmpty($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is true.\n         */\n        public function toBeTrue(): Expectation\n        {\n            Assert::assertTrue($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is false.\n         */\n        public function toBeFalse(): Expectation\n        {\n            Assert::assertFalse($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is greater than $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeGreaterThan($expected): Expectation\n        {\n            Assert::assertGreaterThan($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is greater than or equal to $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeGreaterThanOrEqual($expected): Expectation\n        {\n            Assert::assertGreaterThanOrEqual($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is less than or equal to $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeLessThan($expected): Expectation\n        {\n            Assert::assertLessThan($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is less than $expected.\n         *\n         * @param int|float $expected\n         */\n        public function toBeLessThanOrEqual($expected): Expectation\n        {\n            Assert::assertLessThanOrEqual($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that $needle is an element of the value.\n         *\n         * @param mixed $needle\n         */\n        public function toContain($needle): Expectation\n        {\n            if (is_string($this->value)) {\n                Assert::assertStringContainsString($needle, $this->value);\n            } else {\n                Assert::assertContains($needle, $this->value);\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value starts with $expected.\n         */\n        public function toStartWith(string $expected): Expectation\n        {\n            Assert::assertStringStartsWith($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value ends with $expected.\n         */\n        public function toEndWith(string $expected): Expectation\n        {\n            Assert::assertStringEndsWith($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that $count matches the number of elements of the value.\n         */\n        public function toHaveCount(int $count): Expectation\n        {\n            Assert::assertCount($count, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value contains the property $name.\n         *\n         * @param mixed $value\n         */\n        public function toHaveProperty(string $name, $value = null): Expectation\n        {\n            $this->toBeObject();\n\n            Assert::assertTrue(property_exists($this->value, $name));\n\n            if (func_num_args() > 1) {\n                /* @phpstan-ignore-next-line */\n                Assert::assertEquals($value, $this->value->{$name});\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that two variables have the same value.\n         *\n         * @param mixed $expected\n         */\n        public function toEqual($expected): Expectation\n        {\n            Assert::assertEquals($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that two variables have the same value.\n         * The contents of $expected and the $this->value are\n         * canonicalized before they are compared. For instance, when the two\n         * variables $expected and $this->value are arrays, then these arrays\n         * are sorted before they are compared. When $expected and $this->value\n         * are objects, each object is converted to an array containing all\n         * private, protected and public attributes.\n         *\n         * @param mixed $expected\n         */\n        public function toEqualCanonicalizing($expected): Expectation\n        {\n            Assert::assertEqualsCanonicalizing($expected, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the absolute difference between the value and $expected\n         * is lower than $delta.\n         *\n         * @param mixed $expected\n         */\n        public function toEqualWithDelta($expected, float $delta): Expectation\n        {\n            Assert::assertEqualsWithDelta($expected, $this->value, $delta);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is infinite.\n         */\n        public function toBeInfinite(): Expectation\n        {\n            Assert::assertInfinite($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is an instance of $class.\n         *\n         * @param string $class\n         */\n        public function toBeInstanceOf($class): Expectation\n        {\n            /* @phpstan-ignore-next-line */\n            Assert::assertInstanceOf($class, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is an array.\n         */\n        public function toBeArray(): Expectation\n        {\n            Assert::assertIsArray($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type bool.\n         */\n        public function toBeBool(): Expectation\n        {\n            Assert::assertIsBool($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type callable.\n         */\n        public function toBeCallable(): Expectation\n        {\n            Assert::assertIsCallable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type float.\n         */\n        public function toBeFloat(): Expectation\n        {\n            Assert::assertIsFloat($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type int.\n         */\n        public function toBeInt(): Expectation\n        {\n            Assert::assertIsInt($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type iterable.\n         */\n        public function toBeIterable(): Expectation\n        {\n            Assert::assertIsIterable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type numeric.\n         */\n        public function toBeNumeric(): Expectation\n        {\n            Assert::assertIsNumeric($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type object.\n         */\n        public function toBeObject(): Expectation\n        {\n            Assert::assertIsObject($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type resource.\n         */\n        public function toBeResource(): Expectation\n        {\n            Assert::assertIsResource($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type scalar.\n         */\n        public function toBeScalar(): Expectation\n        {\n            Assert::assertIsScalar($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is of type string.\n         */\n        public function toBeString(): Expectation\n        {\n            Assert::assertIsString($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a JSON string.\n         */\n        public function toBeJson(): Expectation\n        {\n            Assert::assertIsString($this->value);\n            Assert::assertJson($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is NAN.\n         */\n        public function toBeNan(): Expectation\n        {\n            Assert::assertNan($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is null.\n         */\n        public function toBeNull(): Expectation\n        {\n            Assert::assertNull($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value array has the provided $key.\n         *\n         * @param string|int $key\n         * @param mixed      $value\n         */\n        public function toHaveKey($key, $value = null): Expectation\n        {\n            if (is_object($this->value) && method_exists($this->value, 'toArray')) {\n                $array = $this->value->toArray();\n            } else {\n                $array = (array) $this->value;\n            }\n\n            Assert::assertArrayHasKey($key, $array);\n\n            if (func_num_args() > 1) {\n                Assert::assertEquals($value, $array[$key]);\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value array has the provided $keys.\n         *\n         * @param array<int, int|string> $keys\n         */\n        public function toHaveKeys(array $keys): Expectation\n        {\n            foreach ($keys as $key) {\n                $this->toHaveKey($key);\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a directory.\n         */\n        public function toBeDirectory(): Expectation\n        {\n            Assert::assertDirectoryExists($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a directory and is readable.\n         */\n        public function toBeReadableDirectory(): Expectation\n        {\n            Assert::assertDirectoryIsReadable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a directory and is writable.\n         */\n        public function toBeWritableDirectory(): Expectation\n        {\n            Assert::assertDirectoryIsWritable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a file.\n         */\n        public function toBeFile(): Expectation\n        {\n            Assert::assertFileExists($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a file and is readable.\n         */\n        public function toBeReadableFile(): Expectation\n        {\n            Assert::assertFileIsReadable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value is a file and is writable.\n         */\n        public function toBeWritableFile(): Expectation\n        {\n            Assert::assertFileIsWritable($this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value array matches the given array subset.\n         *\n         * @param array<int|string, mixed> $array\n         */\n        public function toMatchArray($array): Expectation\n        {\n            if (is_object($this->value) && method_exists($this->value, 'toArray')) {\n                $valueAsArray = $this->value->toArray();\n            } else {\n                $valueAsArray = (array) $this->value;\n            }\n\n            foreach ($array as $key => $value) {\n                Assert::assertArrayHasKey($key, $valueAsArray);\n\n                Assert::assertEquals(\n                    $value,\n                    $valueAsArray[$key],\n                    sprintf(\n                        'Failed asserting that an array has a key %s with the value %s.',\n                        $this->export($key),\n                        $this->export($valueAsArray[$key]),\n                    ),\n                );\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value object matches a subset\n         * of the properties of an given object.\n         *\n         * @param array<string, mixed>|object $object\n         */\n        public function toMatchObject($object): Expectation\n        {\n            foreach ((array) $object as $property => $value) {\n                Assert::assertTrue(property_exists($this->value, $property));\n\n                /* @phpstan-ignore-next-line */\n                $propertyValue = $this->value->{$property};\n                Assert::assertEquals(\n                    $value,\n                    $propertyValue,\n                    sprintf(\n                        'Failed asserting that an object has a property %s with the value %s.',\n                        $this->export($property),\n                        $this->export($propertyValue),\n                    ),\n                );\n            }\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value matches a regular expression.\n         */\n        public function toMatch(string $expression): Expectation\n        {\n            Assert::assertMatchesRegularExpression($expression, $this->value);\n\n            return $this;\n        }\n\n        /**\n         * Asserts that the value matches a constraint.\n         */\n        public function toMatchConstraint(Constraint $constraint): Expectation\n        {\n            Assert::assertThat($this->value, $constraint);\n\n            return $this;\n        }\n\n        /**\n         * Exports the given value.\n         *\n         * @param mixed $value\n         */\n        private function export($value): string\n        {\n            if ($this->exporter === null) {\n                $this->exporter = new Exporter();\n            }\n\n            return $this->exporter->export($value);\n        }\n\n        /**\n         * Dynamically calls methods on the class without any arguments.\n         *\n         * @return Expectation\n         */\n        public function __get(string $name)\n        {\n            /* @phpstan-ignore-next-line */\n            return $this->{$name}();\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/templates/describe.after.php",
    "content": "<?php\n\ntest(\"a\", function() {\n\n});\n\ndescribe(\"b\", function () {\n\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/templates/describe.php",
    "content": "<?php\n\ntest(\"a\", function() {\n\n});\n\n\"b\".describe<caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/templates/it.after.php",
    "content": "<?php\n\ntest(\"a\", function() {\n\n});\n\nit(\"b\", function () {\n\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/templates/it.php",
    "content": "<?php\n\ntest(\"a\", function() {\n\n});\n\n\"b\".it<caret>"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/TestCase.php",
    "content": "<?php\n\nnamespace PHPUnit\\Framework;\n\nabstract class TestCase {\n    protected $protectedField;\n\n    /**\n     * @param string $exception\n     */\n    public function expectException($exception) {}\n\n    /**\n     * @param int|string $code\n     */\n    public function expectExceptionCode($code){}\n\n    protected function someProtectedMethod(){}\n\n    public static function someStaticMethod(){}\n}"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/expectCallCompletion.php",
    "content": "<?php\n\nexpect()->extend('someExtend', function ($min, $max) {\n    return $this->toBeGreaterThanOrEqual($min)\n        ->toBeLessThanOrEqual($max);\n});\n\ntest('numeric ranges', function () {\n    expect(100)-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/expectCallCompletionChainedNotMethod.php",
    "content": "<?php\n\nexpect()->extend('someExtend', function ($min, $max) {\n    return $this->toBeGreaterThanOrEqual($min)\n        ->toBeLessThanOrEqual($max);\n});\n\ntest('numeric ranges', function () {\n    expect(100)->toBeInt()\n        ->not()\n        -><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/expectCallCompletionChainedNotProperty.php",
    "content": "<?php\n\nexpect()->extend('someExtend', function ($min, $max) {\n    return $this->toBeGreaterThanOrEqual($min)\n        ->toBeLessThanOrEqual($max);\n});\n\ntest('numeric ranges', function () {\n    expect(100)->toBeInt()\n        ->not\n        -><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/expectExtendCallOnNonExpectFunction.php",
    "content": "<?php\n\nexpect()->extend('toBeWithinRange', function ($min, $max) {\n    return $this->toBeGreaterThanOrEqual($min)\n        ->toBeLessThanOrEqual($max);\n});\n\ntest('numeric ranges', function () {\n    notExpect(100)->toBeWithinRange(90, 110)-><caret>;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/expectExtendReturnType.php",
    "content": "<?php\n\nexpect()->extend('someExtend', function ($min, $max) {\n    return $this->toBeGreaterThanOrEqual($min)\n        ->toBeLessThanOrEqual($max);\n});\n\ntest('numeric ranges', function () {\n    expect(100)->someExtend(90, 110)-><caret>;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/expectInvalidExtendNoReturnType.php",
    "content": "<?php\n\ntest('numeric ranges', function () {\n    expect(100)->toNonExtendMethod(90, 110)-><caret>;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/expect/extendCallOnChainedExpectation.php",
    "content": "<?php\n\nexpect()->extend('toChained', function ($min, $max) {\n    return $this->toBeGreaterThanOrEqual($min)\n        ->toBeLessThanOrEqual($max);\n});\n\ntest('numeric ranges', function () {\n    expect(100)\n        ->toBe(100)\n        ->toChained(90, 110)-><caret>;\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/function/testTest.php",
    "content": "<?php\n\ntest('has home', function () {})-><caret>\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/this/beforeEach.php",
    "content": "<?php\n\nbeforeEach(function () {\n    $this-><caret>\n});"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/this/itShortLambdaTest.php",
    "content": "<?php\n\nit('has home', fn () => $this-><caret> );\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/this/itTest.php",
    "content": "<?php\n\nit('has home', function () {\n    $this-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/this/testTest.php",
    "content": "<?php\n\ntest('has home', function () {\n    $this-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/thisField/afterEach.php",
    "content": "<?php\n\nclass Foo {\n    public int $a;\n\n    public function b(){}\n}\n\nbeforeEach(function () {\n    $this->foo = new Foo();\n});\n\nafterEach('has home', function () {\n    $this->foo-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/thisField/afterEachNamespace.php",
    "content": "<?php\n\nnamespace Tests\\Works;\n\nclass Foo {\n    public int $a;\n\n    public function b(){}\n}\n\nbeforeEach(function () {\n    $this->foo = new Foo();\n});\n\nafterEach('has home', function () {\n    $this->foo-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/thisField/beforeEach.php",
    "content": "<?php\n\nclass Foo {\n    public int $a;\n\n    public function b(){}\n}\n\nbeforeEach(function () {\n    $this->foo = new Foo();\n});\n\nit('has home', function () {\n    $this->foo-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/thisField/beforeEachCompletion.php",
    "content": "<?php\n\nbeforeEach(function () {\n    $this->foo = 1;\n});\n\nit('has home', function () {\n    $this-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/thisField/beforeEachNamespace.php",
    "content": "<?php\n\nnamespace Tests\\Works;\n\nclass Foo {\n    public int $a;\n\n    public function b(){}\n}\n\nbeforeEach(function () {\n    $this->foo = new Foo();\n});\n\nit('has home', function () {\n    $this->foo-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/types/thisField/beforeEachNamespaceCompletion.php",
    "content": "<?php\n\nnamespace Tests\\Works;\n\nbeforeEach(function () {\n    $this->foo = 1;\n});\n\nit('has home', function () {\n    $this-><caret>\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/utilTests/ClassNameResolutionInNamespaceTest.php",
    "content": "<?php\n\nnamespace A {\n    class B {\n    }\n}\n\nuse A;\n\ntest(B::class, function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/utilTests/ClassNameResolutionTest.php",
    "content": "<?php\n\nclass A {\n}\n\ntest(A::class, function () {\n    $this->assertTrue(true);\n});\n"
  },
  {
    "path": "src/test/resources/com/pestphp/pest/utilTests/SimpleTest.php",
    "content": "<?php\n\nit('basic', function () {\n    $this->assertTrue(true);\n});\n"
  }
]